Restructuring
This commit is contained in:
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -0,0 +1,180 @@
|
||||
import os
|
||||
import pandas as pd
|
||||
|
||||
|
||||
def process_single_file(file_path, user_label, interval='1H', threshold=None):
|
||||
"""
|
||||
Process a single step count CSV file into a pivoted daily activity DataFrame.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
file_path : str
|
||||
Path to the CSV file.
|
||||
user_label : int
|
||||
Unique label assigned to the user represented by this file.
|
||||
interval : str, optional
|
||||
Any valid pandas resampling interval (e.g., '1H', '15T', '30min', '5min').
|
||||
threshold : float or None, optional
|
||||
Minimum step count value to include in aggregation.
|
||||
If None, all values are included (no filtering).
|
||||
|
||||
Returns
|
||||
-------
|
||||
pd.DataFrame
|
||||
A DataFrame where each row represents one day of activity with
|
||||
boolean indicators for each time interval, plus temporal and user info.
|
||||
"""
|
||||
|
||||
# Load dataset with flexible column handling
|
||||
df = pd.read_csv(file_path, delimiter=';')
|
||||
|
||||
# Ensure required columns exist
|
||||
required_cols = {'device', 'startDate', 'value'}
|
||||
if not required_cols.issubset(df.columns):
|
||||
raise ValueError(f"Missing required columns in {file_path}: {required_cols - set(df.columns)}")
|
||||
|
||||
# Filter for iPhone devices (ignore NaN safely)
|
||||
iphone_df = df[df['device'].str.contains('iPhone', na=False)].copy()
|
||||
if iphone_df.empty:
|
||||
return pd.DataFrame() # Skip empty or invalid files
|
||||
|
||||
# Convert startDate to datetime
|
||||
iphone_df['startDate'] = pd.to_datetime(
|
||||
iphone_df['startDate'], errors='coerce'
|
||||
)
|
||||
iphone_df.dropna(subset=['startDate'], inplace=True)
|
||||
|
||||
# Round down to the nearest interval dynamically
|
||||
iphone_df['interval_start'] = iphone_df['startDate'].dt.floor(interval)
|
||||
|
||||
# Extract date and time components
|
||||
iphone_df['date'] = iphone_df['interval_start'].dt.date
|
||||
iphone_df['time'] = iphone_df['interval_start'].dt.time
|
||||
|
||||
# Apply threshold filtering if specified
|
||||
if threshold is not None:
|
||||
iphone_df = iphone_df[iphone_df['value'] > threshold]
|
||||
|
||||
# Group by date and time, summing step values within each interval
|
||||
interval_sum = (
|
||||
iphone_df.groupby(['date', 'time'])['value']
|
||||
.sum()
|
||||
.reset_index()
|
||||
)
|
||||
|
||||
# Generate a full time range based on the chosen interval
|
||||
full_time_range = pd.date_range('00:00', '23:59', freq=interval).time
|
||||
|
||||
# Pivot to make one row per date, columns as time intervals
|
||||
pivot_table = interval_sum.pivot(
|
||||
index='date', columns='time', values='value'
|
||||
).fillna(0)
|
||||
|
||||
# Ensure all intervals exist even if missing in data
|
||||
pivot_table = pivot_table.reindex(columns=full_time_range, fill_value=0)
|
||||
|
||||
# Rename columns for clarity
|
||||
pivot_table.columns = [str(col) for col in pivot_table.columns]
|
||||
|
||||
# Reset index to make 'date' a column again
|
||||
pivot_table.reset_index(inplace=True)
|
||||
|
||||
# Add temporal features
|
||||
pivot_table['DayOfWeek'] = pd.to_datetime(pivot_table['date']).dt.day_name()
|
||||
pivot_table['Month'] = pd.to_datetime(pivot_table['date']).dt.month
|
||||
pivot_table['Year'] = pd.to_datetime(pivot_table['date']).dt.year
|
||||
|
||||
# One-hot encode day of week
|
||||
pivot_table = pd.concat(
|
||||
[pivot_table, pd.get_dummies(pivot_table['DayOfWeek'], prefix='DayOfWeek')],
|
||||
axis=1
|
||||
)
|
||||
|
||||
# Convert all time-interval columns to boolean (active or not)
|
||||
for col in pivot_table.columns[1:1 + len(full_time_range)]:
|
||||
pivot_table[col] = pivot_table[col].apply(lambda x: True if x > 0 else False)
|
||||
|
||||
# Add user identifier
|
||||
pivot_table['user'] = user_label
|
||||
|
||||
# Drop original DayOfWeek (we have the one-hot encoded version)
|
||||
pivot_table.drop(columns=['DayOfWeek'], inplace=True)
|
||||
|
||||
return pivot_table
|
||||
|
||||
|
||||
def process_stepcount_files(input_folders, output_folder,
|
||||
files_to_skip=None, interval='1H', threshold=None):
|
||||
"""
|
||||
Process multiple step count CSV files from given folders into one aggregated Excel dataset.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
input_folders : list of str
|
||||
List of folders to scan recursively for CSV files.
|
||||
output_folder : str
|
||||
Folder path where the combined Excel file will be saved.
|
||||
files_to_skip : set or list of str, optional
|
||||
Filenames to ignore during processing.
|
||||
interval : str, optional
|
||||
Any valid pandas resampling interval.
|
||||
threshold : float or None, optional
|
||||
Minimum value for step count inclusion. If None, all values are used.
|
||||
|
||||
Returns
|
||||
-------
|
||||
pd.DataFrame
|
||||
Combined DataFrame containing all processed user data.
|
||||
"""
|
||||
|
||||
# Ensure skip list is a set for fast lookup
|
||||
files_to_skip = set(files_to_skip or [])
|
||||
|
||||
# Collect all CSV file paths
|
||||
file_paths = []
|
||||
for folder in input_folders:
|
||||
for root, _, files in os.walk(folder):
|
||||
for fname in files:
|
||||
if fname.endswith('.csv') and fname not in files_to_skip:
|
||||
file_paths.append(os.path.join(root, fname))
|
||||
|
||||
# Assign user labels
|
||||
user_labels = list(range(len(file_paths)))
|
||||
|
||||
# Process each file
|
||||
processed_dfs = []
|
||||
for file_path, user_label in zip(file_paths, user_labels):
|
||||
df = process_single_file(file_path, user_label, interval, threshold)
|
||||
if not df.empty:
|
||||
processed_dfs.append(df)
|
||||
|
||||
# Combine all processed data
|
||||
if not processed_dfs:
|
||||
raise ValueError("No valid data files found for processing.")
|
||||
combined_df = pd.concat(processed_dfs, ignore_index=True)
|
||||
|
||||
# Create output filename dynamically
|
||||
threshold_label = (
|
||||
f"threshold{int(threshold)}" if threshold is not None else "nothreshold"
|
||||
)
|
||||
interval_label = interval.replace(' ', '').replace(':', '')
|
||||
output_filename = f"combined_aggregated_data_{interval_label}_{threshold_label}.xlsx"
|
||||
output_path = os.path.join(output_folder, output_filename)
|
||||
|
||||
# Save to Excel
|
||||
os.makedirs(output_folder, exist_ok=True)
|
||||
combined_df.to_excel(output_path, index=False)
|
||||
|
||||
return combined_df
|
||||
|
||||
|
||||
# Example usage:
|
||||
# combined_df = process_stepcount_files(
|
||||
# input_folders=['/path/to/data/folder'],
|
||||
# output_folder='/path/to/output/folder',
|
||||
# files_to_skip={'StepCount06.csv', 'StepCount10.csv'},
|
||||
# interval='30T', # Any valid pandas frequency, e.g. '5T', '10T', '2H', etc.
|
||||
# threshold=25
|
||||
# )
|
||||
|
||||
process_stepcount_files(["Step_Data_Project_India/Rest_of_the_World", "Step_Data_Project_India/Europe"], "Step_Data_Project_India/OuptutIndiaTest", interval="1H")
|
||||
@@ -0,0 +1,36 @@
|
||||
import data_preprocessing
|
||||
|
||||
# Example usage:
|
||||
# combined_df = process_stepcount_files(
|
||||
# input_folders=[
|
||||
# '/content/drive/My Drive/Data/iOS',
|
||||
# '/content/drive/My Drive/Data/Watch'
|
||||
# ],
|
||||
# output_folder='/content/drive/My Drive/Data/Results',
|
||||
# files_to_skip={
|
||||
# 'StepCount06.csv', 'StepCount10.csv', 'StepCount12.csv',
|
||||
# 'StepCount13.csv', 'StepCount15.csv', 'StepCount17.csv',
|
||||
# 'StepCount18.csv', 'StepCount20.csv', 'StepCount24.csv',
|
||||
# 'StepCount27.csv', 'StepCount31.csv', 'StepCount32.csv',
|
||||
# 'StepCount42.csv', 'StepCount46.csv'
|
||||
# },
|
||||
# interval='15T', # or '1H'
|
||||
# threshold=25 # or None
|
||||
# )
|
||||
|
||||
input_folders=[
|
||||
'Step_Data_Project_India/Europe/Europe',
|
||||
'Step_Data_Project_India/Rest_of_the_World'
|
||||
]
|
||||
output_folder='Step_Data_Project_India/Preprocessing_Results'
|
||||
files_to_skip={
|
||||
'StepCount06.csv', 'StepCount10.csv', 'StepCount12.csv',
|
||||
'StepCount13.csv', 'StepCount15.csv', 'StepCount17.csv',
|
||||
'StepCount18.csv', 'StepCount20.csv', 'StepCount24.csv',
|
||||
'StepCount27.csv', 'StepCount31.csv', 'StepCount32.csv',
|
||||
'StepCount42.csv', 'StepCount46.csv'
|
||||
}
|
||||
interval='15T'
|
||||
threshold=25
|
||||
|
||||
combined_df = data_preprocessing.process_stepcount_files(input_folders, output_folder, files_to_skip, interval, threshold)
|
||||
@@ -0,0 +1,987 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"metadata": {
|
||||
"_cell_guid": "b1076dfc-b9ad-4769-8c92-a6c4dae69d19",
|
||||
"_uuid": "8f2839f25d086af736a60e9eeb907d3b93b6e0e5",
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# This Python 3 environment comes with many helpful analytics libraries installed\n",
|
||||
"# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n",
|
||||
"# For example, here's several helpful packages to load\n",
|
||||
"\n",
|
||||
"import numpy as np # linear algebra\n",
|
||||
"import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n",
|
||||
"\n",
|
||||
"# Input data files are available in the read-only \"../input/\" directory\n",
|
||||
"# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n",
|
||||
"\n",
|
||||
"import os\n",
|
||||
"for dirname, _, filenames in os.walk('/kaggle/input'):\n",
|
||||
" for filename in filenames:\n",
|
||||
" print(os.path.join(dirname, filename))\n",
|
||||
"\n",
|
||||
"# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n",
|
||||
"# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"collapsed": true,
|
||||
"execution": {
|
||||
"iopub.execute_input": "2025-05-02T07:51:57.538752Z",
|
||||
"iopub.status.busy": "2025-05-02T07:51:57.538555Z",
|
||||
"iopub.status.idle": "2025-05-02T08:46:51.909800Z",
|
||||
"shell.execute_reply": "2025-05-02T08:46:51.909147Z",
|
||||
"shell.execute_reply.started": "2025-05-02T07:51:57.538734Z"
|
||||
},
|
||||
"jupyter": {
|
||||
"outputs_hidden": true
|
||||
},
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "ModuleNotFoundError",
|
||||
"evalue": "No module named 'sklearn'",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
|
||||
"\u001b[0;31mModuleNotFoundError\u001b[0m Traceback (most recent call last)",
|
||||
"Cell \u001b[0;32mIn[6], line 11\u001b[0m\n\u001b[1;32m 9\u001b[0m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mkeras_tuner\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mas\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mkt\u001b[39;00m\n\u001b[1;32m 10\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01mkeras_tuner\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m RandomSearch\n\u001b[0;32m---> 11\u001b[0m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;21;01msklearn\u001b[39;00m\u001b[38;5;21;01m.\u001b[39;00m\u001b[38;5;21;01mmetrics\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m accuracy_score\n\u001b[1;32m 13\u001b[0m \u001b[38;5;66;03m# === Clean previous tuning directory ===\u001b[39;00m\n\u001b[1;32m 14\u001b[0m shutil\u001b[38;5;241m.\u001b[39mrmtree(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m/kaggle/working/my_dir\u001b[39m\u001b[38;5;124m\"\u001b[39m, ignore_errors\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)\n",
|
||||
"\u001b[0;31mModuleNotFoundError\u001b[0m: No module named 'sklearn'"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import numpy as np\n",
|
||||
"import pandas as pd\n",
|
||||
"import shutil\n",
|
||||
"import os\n",
|
||||
"from tensorflow.keras.models import Sequential\n",
|
||||
"from tensorflow.keras.layers import LSTM, Dense, Dropout,GRU,Bidirectional\n",
|
||||
"from tensorflow.keras.optimizers import Adam\n",
|
||||
"from tensorflow.keras.callbacks import ReduceLROnPlateau, EarlyStopping\n",
|
||||
"import keras_tuner as kt\n",
|
||||
"from keras_tuner import RandomSearch\n",
|
||||
"from sklearn.metrics import accuracy_score\n",
|
||||
"\n",
|
||||
"# === Clean previous tuning directory ===\n",
|
||||
"shutil.rmtree(\"/kaggle/working/my_dir\", ignore_errors=True)\n",
|
||||
"\n",
|
||||
"# === Load dataset ===\n",
|
||||
"file_path = '/kaggle/input/32usrs/ALLUSERS32_15MIN_WITHOUTTHREHOLD.xlsx' \n",
|
||||
"\n",
|
||||
"df = pd.read_excel(file_path)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# === Helper functions for scenario selection ===\n",
|
||||
"def get_user_input_for_scenario(scenario_type):\n",
|
||||
" print(f\"\\nPlease define your custom {scenario_type} scenario:\")\n",
|
||||
" years_input = input(f\"Enter {scenario_type} years (comma-separated, e.g., 2017,2018): \").strip()\n",
|
||||
" years = list(map(int, years_input.split(',')))\n",
|
||||
" years_months = []\n",
|
||||
" for year in years:\n",
|
||||
" months_input = input(f\"Enter months for year {year} (comma-separated, e.g., 1,2,3): \").strip()\n",
|
||||
" months = list(map(int, months_input.split(',')))\n",
|
||||
" years_months.append((year, months))\n",
|
||||
" return years_months\n",
|
||||
"\n",
|
||||
"def display_warning_about_2020_data():\n",
|
||||
" print(\"\\n⚠️ Warning: 2020 data after February is excluded due to COVID-19.\")\n",
|
||||
" print(\"✅ Only Jan and Feb 2020 are used for testing. Do not use them in training/validation.\")\n",
|
||||
"\n",
|
||||
"def display_warnings_for_scenarios(scenario_type):\n",
|
||||
" if scenario_type == \"training\":\n",
|
||||
" print(\"\\n⚠️ Predefined Training Scenarios (for reference only):\")\n",
|
||||
" for name, scenario in predefined_training_scenarios.items():\n",
|
||||
" parts = [f\"{year}-{months}\" for year, months in scenario['years_months']]\n",
|
||||
" print(f\" {name}: {', '.join(parts)}\")\n",
|
||||
" elif scenario_type == \"validation\":\n",
|
||||
" print(\"\\n⚠️ Predefined Validation Scenario:\")\n",
|
||||
" for name, scenario in predefined_validation_scenarios.items():\n",
|
||||
" parts = [f\"{year}-{months}\" for year, months in scenario['years_months']]\n",
|
||||
" print(f\" {name}: {', '.join(parts)}\")\n",
|
||||
" print(\" - This uses Oct, Nov, Dec of 2019\")\n",
|
||||
"\n",
|
||||
"predefined_training_scenarios = {\n",
|
||||
" \"Scenario 1\": {\"years_months\": [(2018, list(range(1, 13))), (2019, list(range(1, 10)))]},\n",
|
||||
" \"Scenario 2\": {\"years_months\": [(2017, list(range(1, 13))), (2018, list(range(1, 13))), (2019, list(range(1, 10)))]}\n",
|
||||
"}\n",
|
||||
"predefined_validation_scenarios = {\n",
|
||||
" \"Scenario A\": {\"years_months\": [(2019, [10, 11, 12])]}\n",
|
||||
"}\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# === Get user-defined training and validation scenarios ===\n",
|
||||
"print(\"=== Training Scenario Setup ===\")\n",
|
||||
"display_warning_about_2020_data()\n",
|
||||
"display_warnings_for_scenarios(\"training\")\n",
|
||||
"training_scenario = get_user_input_for_scenario(\"training\")\n",
|
||||
"\n",
|
||||
"print(\"\\n=== Validation Scenario Setup ===\")\n",
|
||||
"display_warning_about_2020_data()\n",
|
||||
"display_warnings_for_scenarios(\"validation\")\n",
|
||||
"validation_scenario = get_user_input_for_scenario(\"validation\")\n",
|
||||
"\n",
|
||||
"# === Filter and preprocess data ===\n",
|
||||
"def filter_data(df, scenario):\n",
|
||||
" filtered = pd.DataFrame()\n",
|
||||
" for year, months in scenario:\n",
|
||||
" filtered = pd.concat([filtered, df[(df['Year'] == year) & (df['Month'].isin(months))]])\n",
|
||||
" return filtered.drop(columns=['Month', 'Year', 'date', 'DayOfWeek']) \n",
|
||||
"\n",
|
||||
"data = filter_data(df, training_scenario)\n",
|
||||
"data_val = filter_data(df, validation_scenario)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"\n",
|
||||
"# === Organize by user ===\n",
|
||||
"df_sorted = data.sort_values(by='user').reset_index(drop=True)\n",
|
||||
"df_sorted_val = data_val.sort_values(by='user').reset_index(drop=True)\n",
|
||||
"users = df_sorted['user'].unique()\n",
|
||||
"users_val = df_sorted_val['user'].unique()\n",
|
||||
"\n",
|
||||
"user_data = {user: df_sorted[df_sorted['user'] == user] for user in users}\n",
|
||||
"user_data_val = {user: df_sorted_val[df_sorted_val['user'] == user] for user in users_val}\n",
|
||||
"\n",
|
||||
"# === Callbacks ===\n",
|
||||
"early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)\n",
|
||||
"lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=5, verbose=1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# === Model tuning and training loop ===\n",
|
||||
"best_models = {}\n",
|
||||
"\n",
|
||||
"for sequence_length in range(20, 30, 5):\n",
|
||||
" print(f\"\\n=== Training for Sequence Length: {sequence_length} ===\")\n",
|
||||
"\n",
|
||||
" # Training data\n",
|
||||
" X, y = [], []\n",
|
||||
" for user, data in user_data.items():\n",
|
||||
" features = data.drop('user', axis=1).values\n",
|
||||
" labels = data['user'].values\n",
|
||||
" for i in range(len(features) - sequence_length):\n",
|
||||
" X.append(features[i:i + sequence_length])\n",
|
||||
" y.append(labels[i + sequence_length])\n",
|
||||
" X = np.array(X)\n",
|
||||
" y = np.array(y)\n",
|
||||
"\n",
|
||||
" # Validation data\n",
|
||||
" X_val, y_val = [], []\n",
|
||||
" for user, data in user_data_val.items():\n",
|
||||
" features = data.drop('user', axis=1).values\n",
|
||||
" labels = data['user'].values\n",
|
||||
" for i in range(len(features) - sequence_length):\n",
|
||||
" X_val.append(features[i:i + sequence_length])\n",
|
||||
" y_val.append(labels[i + sequence_length])\n",
|
||||
" X_val = np.array(X_val)\n",
|
||||
" y_val = np.array(y_val)\n",
|
||||
"\n",
|
||||
" if X.shape[0] == 0 or X_val.shape[0] == 0:\n",
|
||||
" print(f\"⚠️ Skipped sequence length {sequence_length} due to insufficient data.\")\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" n_features = X.shape[2]\n",
|
||||
"\n",
|
||||
" def build_model(hp):\n",
|
||||
" model = Sequential()\n",
|
||||
" model.add(Bidirectional(LSTM(units=hp.Int('units', 32, 256, step=2),\n",
|
||||
" input_shape=(sequence_length, n_features))))\n",
|
||||
" model.add(Dropout(hp.Float('dropout_rate', 0.1, 0.5, step=0.1)))\n",
|
||||
" model.add(Dense(len(users), activation='softmax'))\n",
|
||||
" model.compile(\n",
|
||||
" optimizer=Adam(learning_rate=hp.Choice('learning_rate', [1e-2, 1e-3, 1e-4])),\n",
|
||||
" loss='sparse_categorical_crossentropy',\n",
|
||||
" metrics=['accuracy']\n",
|
||||
" )\n",
|
||||
" return model\n",
|
||||
"\n",
|
||||
" tuner = RandomSearch(\n",
|
||||
" build_model,\n",
|
||||
" objective='val_loss',\n",
|
||||
" max_trials=30,\n",
|
||||
" executions_per_trial=2,\n",
|
||||
" directory='/kaggle/working/my_dir',\n",
|
||||
" project_name=f'lstm_seq_{sequence_length}'\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" tuner.search(X, y, epochs=30, validation_data=(X_val, y_val),\n",
|
||||
" callbacks=[early_stopping, lr_scheduler], verbose=1)\n",
|
||||
"\n",
|
||||
" best_hps = tuner.get_best_hyperparameters(1)[0]\n",
|
||||
" best_model = tuner.hypermodel.build(best_hps)\n",
|
||||
" best_model.fit(X, y, epochs=30, validation_data=(X_val, y_val),\n",
|
||||
" callbacks=[early_stopping, lr_scheduler], verbose=0)\n",
|
||||
"\n",
|
||||
" best_models[sequence_length] = {\n",
|
||||
" 'model': best_model,\n",
|
||||
" 'best_hyperparameters': {\n",
|
||||
" 'units': best_hps.get('units'),\n",
|
||||
" 'dropout_rate': best_hps.get('dropout_rate'),\n",
|
||||
" 'learning_rate': best_hps.get('learning_rate')\n",
|
||||
" }\n",
|
||||
" }"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"\n",
|
||||
"# === Get test scenario input ===\n",
|
||||
"def get_user_input_for_test():\n",
|
||||
" print(\"\\n=== Testing Scenario Setup ===\")\n",
|
||||
" print(\"⚠️ Only January and February of 2020 were used for testing in predefined setup.\")\n",
|
||||
" print(\"⚠️ Avoid using 2020 data after February due to COVID-19 impact.\\n\")\n",
|
||||
" years_input = input(\"Enter test years (comma-separated, e.g., 2020): \").strip()\n",
|
||||
" years = list(map(int, years_input.split(',')))\n",
|
||||
" years_months = []\n",
|
||||
" for year in years:\n",
|
||||
" months_input = input(f\"Enter months for year {year} (comma-separated, e.g., 1,2): \").strip()\n",
|
||||
" months = list(map(int, months_input.split(',')))\n",
|
||||
" years_months.append((year, months))\n",
|
||||
" return years_months\n",
|
||||
"\n",
|
||||
"def filter_test_data(df, scenario):\n",
|
||||
" data_parts = []\n",
|
||||
" for year, months in scenario:\n",
|
||||
" part = df[(df['Year'] == year) & (df['Month'].isin(months))]\n",
|
||||
" data_parts.append(part)\n",
|
||||
" return pd.concat(data_parts, ignore_index=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"metadata": {
|
||||
"execution": {
|
||||
"iopub.execute_input": "2025-05-02T08:53:17.334789Z",
|
||||
"iopub.status.busy": "2025-05-02T08:53:17.334489Z",
|
||||
"iopub.status.idle": "2025-05-02T08:53:17.344855Z",
|
||||
"shell.execute_reply": "2025-05-02T08:53:17.344176Z",
|
||||
"shell.execute_reply.started": "2025-05-02T08:53:17.334766Z"
|
||||
},
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import pandas as pd\n",
|
||||
"import os\n",
|
||||
"\n",
|
||||
"def evaluate_model_on_test_data(model, test_df, sequence_length, excel_writer):\n",
|
||||
" print(\"\\n🧪 Evaluating on Test Data...\")\n",
|
||||
" test_df = test_df.drop(columns=['Month', 'Year', 'date', 'DayOfWeek'])\n",
|
||||
" test_df = test_df.sort_values(by='user').reset_index(drop=True)\n",
|
||||
"\n",
|
||||
" users = test_df['user'].unique()\n",
|
||||
" results = []\n",
|
||||
" accuracy_above_50 = 0\n",
|
||||
"\n",
|
||||
" for user in users:\n",
|
||||
" user_df = test_df[test_df['user'] == user]\n",
|
||||
" X, y_true = [], []\n",
|
||||
" user_features = user_df.drop(columns=['user']).values\n",
|
||||
" user_labels = user_df['user'].values\n",
|
||||
"\n",
|
||||
" if len(user_df) <= sequence_length:\n",
|
||||
" print(f\"Skipping User {user} (not enough data for sequence length {sequence_length})\")\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" for i in range(len(user_df) - sequence_length):\n",
|
||||
" seq_x = user_features[i:i + sequence_length]\n",
|
||||
" seq_y = user_labels[i + sequence_length]\n",
|
||||
" X.append(seq_x)\n",
|
||||
" y_true.append(seq_y)\n",
|
||||
"\n",
|
||||
" X = np.array(X)\n",
|
||||
" y_true = np.array(y_true)\n",
|
||||
"\n",
|
||||
" if len(X) == 0:\n",
|
||||
" continue\n",
|
||||
"\n",
|
||||
" y_pred = model.predict(X, verbose=0)\n",
|
||||
" y_pred_classes = np.argmax(y_pred, axis=1)\n",
|
||||
"\n",
|
||||
" unique_pred, counts_pred = np.unique(y_pred_classes, return_counts=True)\n",
|
||||
" label_counts_pred = dict(zip(unique_pred, counts_pred))\n",
|
||||
"\n",
|
||||
" unique_true, counts_true = np.unique(y_true, return_counts=True)\n",
|
||||
" label_counts_true = dict(zip(unique_true, counts_true))\n",
|
||||
"\n",
|
||||
" acc = accuracy_score(y_true, y_pred_classes)\n",
|
||||
" if acc > 0.5:\n",
|
||||
" accuracy_above_50 += 1\n",
|
||||
"\n",
|
||||
" # Append result to list\n",
|
||||
" results.append({\n",
|
||||
" 'User': user,\n",
|
||||
" 'Accuracy (%)': acc * 100,\n",
|
||||
" 'Predicted Class Distribution': str(label_counts_pred),\n",
|
||||
" 'Actual Class Distribution': str(label_counts_true)\n",
|
||||
" })\n",
|
||||
"\n",
|
||||
" print(f\"\\n=== User {user} ===\")\n",
|
||||
" print(f\"✅ Accuracy: {acc * 100:.2f}%\")\n",
|
||||
" print(\"📊 Predicted Class Distribution:\", label_counts_pred)\n",
|
||||
" print(\"📌 Actual Class Distribution: \", label_counts_true)\n",
|
||||
"\n",
|
||||
" final_accuracy_percent = (accuracy_above_50 / 32) * 100\n",
|
||||
" print(f\"\\n🟩 Final Evaluation Summary for Sequence Length {sequence_length}:\")\n",
|
||||
" print(f\"Users with >50% Accuracy: {accuracy_above_50} / 32\")\n",
|
||||
" print(f\"✅ Final Success Rate: {final_accuracy_percent:.2f}%\")\n",
|
||||
"\n",
|
||||
" # Append overall stats as a new row\n",
|
||||
" results.append({\n",
|
||||
" 'User': 'TOTAL',\n",
|
||||
" 'Accuracy (%)': '',\n",
|
||||
" 'Predicted Class Distribution': f'Users >50% Acc: {accuracy_above_50}/32',\n",
|
||||
" 'Actual Class Distribution': f'Success Rate: {final_accuracy_percent:.2f}%'\n",
|
||||
" })\n",
|
||||
"\n",
|
||||
" # Save results to Excel sheet\n",
|
||||
" df_results = pd.DataFrame(results)\n",
|
||||
" df_results.to_excel(excel_writer, sheet_name=f\"SeqLen_{sequence_length}\", index=False)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"metadata": {
|
||||
"collapsed": true,
|
||||
"execution": {
|
||||
"iopub.execute_input": "2025-05-02T08:56:14.082755Z",
|
||||
"iopub.status.busy": "2025-05-02T08:56:14.082010Z",
|
||||
"iopub.status.idle": "2025-05-02T08:56:28.518300Z",
|
||||
"shell.execute_reply": "2025-05-02T08:56:28.517562Z",
|
||||
"shell.execute_reply.started": "2025-05-02T08:56:14.082721Z"
|
||||
},
|
||||
"jupyter": {
|
||||
"outputs_hidden": true
|
||||
},
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"=== Testing Scenario Setup ===\n",
|
||||
"⚠️ Only January and February of 2020 were used for testing in predefined setup.\n",
|
||||
"⚠️ Avoid using 2020 data after February due to COVID-19 impact.\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Enter test years (comma-separated, e.g., 2020): 2020\n",
|
||||
"Enter months for year 2020 (comma-separated, e.g., 1,2): 1,2\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"🔍 Testing Model for Sequence Length: 20\n",
|
||||
"\n",
|
||||
"🧪 Evaluating on Test Data...\n",
|
||||
"\n",
|
||||
"=== User 0 ===\n",
|
||||
"✅ Accuracy: 47.50%\n",
|
||||
"📊 Predicted Class Distribution: {0: 19, 18: 9, 24: 7, 26: 1, 30: 3, 31: 1}\n",
|
||||
"📌 Actual Class Distribution: {0: 40}\n",
|
||||
"\n",
|
||||
"=== User 1 ===\n",
|
||||
"✅ Accuracy: 82.50%\n",
|
||||
"📊 Predicted Class Distribution: {1: 33, 31: 7}\n",
|
||||
"📌 Actual Class Distribution: {1: 40}\n",
|
||||
"\n",
|
||||
"=== User 2 ===\n",
|
||||
"✅ Accuracy: 0.00%\n",
|
||||
"📊 Predicted Class Distribution: {6: 2, 12: 12, 17: 13, 30: 12, 31: 1}\n",
|
||||
"📌 Actual Class Distribution: {2: 40}\n",
|
||||
"\n",
|
||||
"=== User 3 ===\n",
|
||||
"✅ Accuracy: 41.03%\n",
|
||||
"📊 Predicted Class Distribution: {3: 16, 6: 1, 12: 8, 29: 13, 30: 1}\n",
|
||||
"📌 Actual Class Distribution: {3: 39}\n",
|
||||
"\n",
|
||||
"=== User 4 ===\n",
|
||||
"✅ Accuracy: 2.50%\n",
|
||||
"📊 Predicted Class Distribution: {2: 1, 4: 1, 8: 2, 9: 3, 18: 11, 23: 3, 26: 16, 29: 1, 30: 1, 31: 1}\n",
|
||||
"📌 Actual Class Distribution: {4: 40}\n",
|
||||
"\n",
|
||||
"=== User 5 ===\n",
|
||||
"✅ Accuracy: 57.50%\n",
|
||||
"📊 Predicted Class Distribution: {2: 5, 5: 23, 23: 2, 29: 6, 30: 3, 31: 1}\n",
|
||||
"📌 Actual Class Distribution: {5: 40}\n",
|
||||
"\n",
|
||||
"=== User 6 ===\n",
|
||||
"✅ Accuracy: 25.00%\n",
|
||||
"📊 Predicted Class Distribution: {6: 10, 17: 1, 30: 5, 31: 24}\n",
|
||||
"📌 Actual Class Distribution: {6: 40}\n",
|
||||
"\n",
|
||||
"=== User 7 ===\n",
|
||||
"✅ Accuracy: 52.50%\n",
|
||||
"📊 Predicted Class Distribution: {7: 21, 10: 3, 11: 14, 18: 2}\n",
|
||||
"📌 Actual Class Distribution: {7: 40}\n",
|
||||
"\n",
|
||||
"=== User 8 ===\n",
|
||||
"✅ Accuracy: 62.50%\n",
|
||||
"📊 Predicted Class Distribution: {8: 25, 23: 1, 29: 8, 30: 6}\n",
|
||||
"📌 Actual Class Distribution: {8: 40}\n",
|
||||
"\n",
|
||||
"=== User 9 ===\n",
|
||||
"✅ Accuracy: 100.00%\n",
|
||||
"📊 Predicted Class Distribution: {9: 40}\n",
|
||||
"📌 Actual Class Distribution: {9: 40}\n",
|
||||
"\n",
|
||||
"=== User 10 ===\n",
|
||||
"✅ Accuracy: 57.50%\n",
|
||||
"📊 Predicted Class Distribution: {10: 23, 11: 15, 30: 2}\n",
|
||||
"📌 Actual Class Distribution: {10: 40}\n",
|
||||
"\n",
|
||||
"=== User 11 ===\n",
|
||||
"✅ Accuracy: 35.00%\n",
|
||||
"📊 Predicted Class Distribution: {1: 1, 10: 15, 11: 14, 12: 1, 14: 4, 15: 2, 16: 2, 25: 1}\n",
|
||||
"📌 Actual Class Distribution: {11: 40}\n",
|
||||
"\n",
|
||||
"=== User 12 ===\n",
|
||||
"✅ Accuracy: 62.50%\n",
|
||||
"📊 Predicted Class Distribution: {3: 1, 12: 25, 26: 14}\n",
|
||||
"📌 Actual Class Distribution: {12: 40}\n",
|
||||
"\n",
|
||||
"=== User 13 ===\n",
|
||||
"✅ Accuracy: 55.00%\n",
|
||||
"📊 Predicted Class Distribution: {10: 3, 11: 3, 12: 2, 13: 22, 16: 1, 21: 9}\n",
|
||||
"📌 Actual Class Distribution: {13: 40}\n",
|
||||
"\n",
|
||||
"=== User 14 ===\n",
|
||||
"✅ Accuracy: 70.00%\n",
|
||||
"📊 Predicted Class Distribution: {0: 1, 14: 28, 16: 2, 18: 7, 25: 2}\n",
|
||||
"📌 Actual Class Distribution: {14: 40}\n",
|
||||
"\n",
|
||||
"=== User 15 ===\n",
|
||||
"✅ Accuracy: 100.00%\n",
|
||||
"📊 Predicted Class Distribution: {15: 40}\n",
|
||||
"📌 Actual Class Distribution: {15: 40}\n",
|
||||
"\n",
|
||||
"=== User 16 ===\n",
|
||||
"✅ Accuracy: 17.50%\n",
|
||||
"📊 Predicted Class Distribution: {15: 20, 16: 7, 18: 13}\n",
|
||||
"📌 Actual Class Distribution: {16: 40}\n",
|
||||
"\n",
|
||||
"=== User 17 ===\n",
|
||||
"✅ Accuracy: 40.00%\n",
|
||||
"📊 Predicted Class Distribution: {0: 2, 16: 6, 17: 16, 18: 1, 28: 1, 31: 14}\n",
|
||||
"📌 Actual Class Distribution: {17: 40}\n",
|
||||
"\n",
|
||||
"=== User 18 ===\n",
|
||||
"✅ Accuracy: 97.50%\n",
|
||||
"📊 Predicted Class Distribution: {0: 1, 18: 39}\n",
|
||||
"📌 Actual Class Distribution: {18: 40}\n",
|
||||
"\n",
|
||||
"=== User 19 ===\n",
|
||||
"✅ Accuracy: 72.50%\n",
|
||||
"📊 Predicted Class Distribution: {1: 3, 6: 7, 19: 29, 22: 1}\n",
|
||||
"📌 Actual Class Distribution: {19: 40}\n",
|
||||
"\n",
|
||||
"=== User 20 ===\n",
|
||||
"✅ Accuracy: 77.50%\n",
|
||||
"📊 Predicted Class Distribution: {2: 8, 20: 31, 26: 1}\n",
|
||||
"📌 Actual Class Distribution: {20: 40}\n",
|
||||
"\n",
|
||||
"=== User 21 ===\n",
|
||||
"✅ Accuracy: 92.50%\n",
|
||||
"📊 Predicted Class Distribution: {21: 37, 24: 3}\n",
|
||||
"📌 Actual Class Distribution: {21: 40}\n",
|
||||
"\n",
|
||||
"=== User 22 ===\n",
|
||||
"✅ Accuracy: 0.00%\n",
|
||||
"📊 Predicted Class Distribution: {8: 4, 9: 2, 23: 1, 29: 27, 30: 1}\n",
|
||||
"📌 Actual Class Distribution: {22: 35}\n",
|
||||
"\n",
|
||||
"=== User 23 ===\n",
|
||||
"✅ Accuracy: 77.50%\n",
|
||||
"📊 Predicted Class Distribution: {3: 9, 23: 31}\n",
|
||||
"📌 Actual Class Distribution: {23: 40}\n",
|
||||
"\n",
|
||||
"=== User 24 ===\n",
|
||||
"✅ Accuracy: 92.50%\n",
|
||||
"📊 Predicted Class Distribution: {21: 3, 24: 37}\n",
|
||||
"📌 Actual Class Distribution: {24: 40}\n",
|
||||
"\n",
|
||||
"=== User 25 ===\n",
|
||||
"✅ Accuracy: 2.50%\n",
|
||||
"📊 Predicted Class Distribution: {2: 14, 12: 11, 23: 1, 25: 1, 29: 4, 30: 9}\n",
|
||||
"📌 Actual Class Distribution: {25: 40}\n",
|
||||
"\n",
|
||||
"=== User 26 ===\n",
|
||||
"✅ Accuracy: 0.00%\n",
|
||||
"📊 Predicted Class Distribution: {12: 18, 18: 3, 21: 13, 24: 6}\n",
|
||||
"📌 Actual Class Distribution: {26: 40}\n",
|
||||
"\n",
|
||||
"=== User 27 ===\n",
|
||||
"✅ Accuracy: 0.00%\n",
|
||||
"📊 Predicted Class Distribution: {12: 38, 21: 1, 24: 1}\n",
|
||||
"📌 Actual Class Distribution: {27: 40}\n",
|
||||
"\n",
|
||||
"=== User 28 ===\n",
|
||||
"✅ Accuracy: 100.00%\n",
|
||||
"📊 Predicted Class Distribution: {28: 40}\n",
|
||||
"📌 Actual Class Distribution: {28: 40}\n",
|
||||
"\n",
|
||||
"=== User 29 ===\n",
|
||||
"✅ Accuracy: 40.00%\n",
|
||||
"📊 Predicted Class Distribution: {12: 12, 26: 1, 29: 16, 30: 11}\n",
|
||||
"📌 Actual Class Distribution: {29: 40}\n",
|
||||
"\n",
|
||||
"=== User 30 ===\n",
|
||||
"✅ Accuracy: 35.00%\n",
|
||||
"📊 Predicted Class Distribution: {12: 1, 18: 9, 23: 5, 25: 3, 26: 3, 29: 2, 30: 14, 31: 3}\n",
|
||||
"📌 Actual Class Distribution: {30: 40}\n",
|
||||
"\n",
|
||||
"=== User 31 ===\n",
|
||||
"✅ Accuracy: 50.00%\n",
|
||||
"📊 Predicted Class Distribution: {12: 2, 18: 18, 31: 20}\n",
|
||||
"📌 Actual Class Distribution: {31: 40}\n",
|
||||
"\n",
|
||||
"🟩 Final Evaluation Summary for Sequence Length 20:\n",
|
||||
"Users with >50% Accuracy: 17 / 32\n",
|
||||
"✅ Final Success Rate: 53.12%\n",
|
||||
"\n",
|
||||
"🔍 Testing Model for Sequence Length: 25\n",
|
||||
"\n",
|
||||
"🧪 Evaluating on Test Data...\n",
|
||||
"\n",
|
||||
"=== User 0 ===\n",
|
||||
"✅ Accuracy: 17.14%\n",
|
||||
"📊 Predicted Class Distribution: {0: 6, 18: 2, 24: 3, 25: 2, 26: 14, 30: 7, 31: 1}\n",
|
||||
"📌 Actual Class Distribution: {0: 35}\n",
|
||||
"\n",
|
||||
"=== User 1 ===\n",
|
||||
"✅ Accuracy: 8.57%\n",
|
||||
"📊 Predicted Class Distribution: {1: 3, 31: 32}\n",
|
||||
"📌 Actual Class Distribution: {1: 35}\n",
|
||||
"\n",
|
||||
"=== User 2 ===\n",
|
||||
"✅ Accuracy: 5.71%\n",
|
||||
"📊 Predicted Class Distribution: {2: 2, 12: 5, 17: 11, 21: 1, 30: 3, 31: 13}\n",
|
||||
"📌 Actual Class Distribution: {2: 35}\n",
|
||||
"\n",
|
||||
"=== User 3 ===\n",
|
||||
"✅ Accuracy: 14.71%\n",
|
||||
"📊 Predicted Class Distribution: {3: 5, 12: 1, 29: 5, 30: 16, 31: 7}\n",
|
||||
"📌 Actual Class Distribution: {3: 34}\n",
|
||||
"\n",
|
||||
"=== User 4 ===\n",
|
||||
"✅ Accuracy: 0.00%\n",
|
||||
"📊 Predicted Class Distribution: {2: 4, 9: 4, 10: 1, 25: 7, 26: 5, 27: 1, 30: 12, 31: 1}\n",
|
||||
"📌 Actual Class Distribution: {4: 35}\n",
|
||||
"\n",
|
||||
"=== User 5 ===\n",
|
||||
"✅ Accuracy: 100.00%\n",
|
||||
"📊 Predicted Class Distribution: {5: 35}\n",
|
||||
"📌 Actual Class Distribution: {5: 35}\n",
|
||||
"\n",
|
||||
"=== User 6 ===\n",
|
||||
"✅ Accuracy: 31.43%\n",
|
||||
"📊 Predicted Class Distribution: {6: 11, 31: 24}\n",
|
||||
"📌 Actual Class Distribution: {6: 35}\n",
|
||||
"\n",
|
||||
"=== User 7 ===\n",
|
||||
"✅ Accuracy: 65.71%\n",
|
||||
"📊 Predicted Class Distribution: {7: 23, 10: 3, 13: 9}\n",
|
||||
"📌 Actual Class Distribution: {7: 35}\n",
|
||||
"\n",
|
||||
"=== User 8 ===\n",
|
||||
"✅ Accuracy: 82.86%\n",
|
||||
"📊 Predicted Class Distribution: {4: 2, 8: 29, 22: 2, 30: 2}\n",
|
||||
"📌 Actual Class Distribution: {8: 35}\n",
|
||||
"\n",
|
||||
"=== User 9 ===\n",
|
||||
"✅ Accuracy: 97.14%\n",
|
||||
"📊 Predicted Class Distribution: {4: 1, 9: 34}\n",
|
||||
"📌 Actual Class Distribution: {9: 35}\n",
|
||||
"\n",
|
||||
"=== User 10 ===\n",
|
||||
"✅ Accuracy: 40.00%\n",
|
||||
"📊 Predicted Class Distribution: {10: 14, 13: 6, 23: 3, 25: 2, 30: 10}\n",
|
||||
"📌 Actual Class Distribution: {10: 35}\n",
|
||||
"\n",
|
||||
"=== User 11 ===\n",
|
||||
"✅ Accuracy: 31.43%\n",
|
||||
"📊 Predicted Class Distribution: {10: 22, 11: 11, 12: 1, 19: 1}\n",
|
||||
"📌 Actual Class Distribution: {11: 35}\n",
|
||||
"\n",
|
||||
"=== User 12 ===\n",
|
||||
"✅ Accuracy: 57.14%\n",
|
||||
"📊 Predicted Class Distribution: {12: 20, 29: 15}\n",
|
||||
"📌 Actual Class Distribution: {12: 35}\n",
|
||||
"\n",
|
||||
"=== User 13 ===\n",
|
||||
"✅ Accuracy: 57.14%\n",
|
||||
"📊 Predicted Class Distribution: {12: 1, 13: 20, 21: 14}\n",
|
||||
"📌 Actual Class Distribution: {13: 35}\n",
|
||||
"\n",
|
||||
"=== User 14 ===\n",
|
||||
"✅ Accuracy: 62.86%\n",
|
||||
"📊 Predicted Class Distribution: {0: 4, 14: 22, 15: 2, 18: 7}\n",
|
||||
"📌 Actual Class Distribution: {14: 35}\n",
|
||||
"\n",
|
||||
"=== User 15 ===\n",
|
||||
"✅ Accuracy: 100.00%\n",
|
||||
"📊 Predicted Class Distribution: {15: 35}\n",
|
||||
"📌 Actual Class Distribution: {15: 35}\n",
|
||||
"\n",
|
||||
"=== User 16 ===\n",
|
||||
"✅ Accuracy: 40.00%\n",
|
||||
"📊 Predicted Class Distribution: {7: 2, 15: 13, 16: 14, 18: 6}\n",
|
||||
"📌 Actual Class Distribution: {16: 35}\n",
|
||||
"\n",
|
||||
"=== User 17 ===\n",
|
||||
"✅ Accuracy: 65.71%\n",
|
||||
"📊 Predicted Class Distribution: {0: 1, 16: 11, 17: 23}\n",
|
||||
"📌 Actual Class Distribution: {17: 35}\n",
|
||||
"\n",
|
||||
"=== User 18 ===\n",
|
||||
"✅ Accuracy: 82.86%\n",
|
||||
"📊 Predicted Class Distribution: {0: 6, 18: 29}\n",
|
||||
"📌 Actual Class Distribution: {18: 35}\n",
|
||||
"\n",
|
||||
"=== User 19 ===\n",
|
||||
"✅ Accuracy: 60.00%\n",
|
||||
"📊 Predicted Class Distribution: {6: 13, 19: 21, 22: 1}\n",
|
||||
"📌 Actual Class Distribution: {19: 35}\n",
|
||||
"\n",
|
||||
"=== User 20 ===\n",
|
||||
"✅ Accuracy: 5.71%\n",
|
||||
"📊 Predicted Class Distribution: {2: 33, 20: 2}\n",
|
||||
"📌 Actual Class Distribution: {20: 35}\n",
|
||||
"\n",
|
||||
"=== User 21 ===\n",
|
||||
"✅ Accuracy: 100.00%\n",
|
||||
"📊 Predicted Class Distribution: {21: 35}\n",
|
||||
"📌 Actual Class Distribution: {21: 35}\n",
|
||||
"\n",
|
||||
"=== User 22 ===\n",
|
||||
"✅ Accuracy: 0.00%\n",
|
||||
"📊 Predicted Class Distribution: {8: 2, 9: 2, 29: 26}\n",
|
||||
"📌 Actual Class Distribution: {22: 30}\n",
|
||||
"\n",
|
||||
"=== User 23 ===\n",
|
||||
"✅ Accuracy: 65.71%\n",
|
||||
"📊 Predicted Class Distribution: {3: 4, 23: 23, 30: 8}\n",
|
||||
"📌 Actual Class Distribution: {23: 35}\n",
|
||||
"\n",
|
||||
"=== User 24 ===\n",
|
||||
"✅ Accuracy: 100.00%\n",
|
||||
"📊 Predicted Class Distribution: {24: 35}\n",
|
||||
"📌 Actual Class Distribution: {24: 35}\n",
|
||||
"\n",
|
||||
"=== User 25 ===\n",
|
||||
"✅ Accuracy: 0.00%\n",
|
||||
"📊 Predicted Class Distribution: {2: 33, 12: 1, 30: 1}\n",
|
||||
"📌 Actual Class Distribution: {25: 35}\n",
|
||||
"\n",
|
||||
"=== User 26 ===\n",
|
||||
"✅ Accuracy: 0.00%\n",
|
||||
"📊 Predicted Class Distribution: {12: 29, 21: 6}\n",
|
||||
"📌 Actual Class Distribution: {26: 35}\n",
|
||||
"\n",
|
||||
"=== User 27 ===\n",
|
||||
"✅ Accuracy: 0.00%\n",
|
||||
"📊 Predicted Class Distribution: {12: 35}\n",
|
||||
"📌 Actual Class Distribution: {27: 35}\n",
|
||||
"\n",
|
||||
"=== User 28 ===\n",
|
||||
"✅ Accuracy: 100.00%\n",
|
||||
"📊 Predicted Class Distribution: {28: 35}\n",
|
||||
"📌 Actual Class Distribution: {28: 35}\n",
|
||||
"\n",
|
||||
"=== User 29 ===\n",
|
||||
"✅ Accuracy: 28.57%\n",
|
||||
"📊 Predicted Class Distribution: {2: 1, 12: 2, 26: 8, 29: 10, 30: 14}\n",
|
||||
"📌 Actual Class Distribution: {29: 35}\n",
|
||||
"\n",
|
||||
"=== User 30 ===\n",
|
||||
"✅ Accuracy: 34.29%\n",
|
||||
"📊 Predicted Class Distribution: {2: 4, 26: 2, 27: 4, 29: 13, 30: 12}\n",
|
||||
"📌 Actual Class Distribution: {30: 35}\n",
|
||||
"\n",
|
||||
"=== User 31 ===\n",
|
||||
"✅ Accuracy: 60.00%\n",
|
||||
"📊 Predicted Class Distribution: {12: 1, 16: 1, 18: 12, 31: 21}\n",
|
||||
"📌 Actual Class Distribution: {31: 35}\n",
|
||||
"\n",
|
||||
"🟩 Final Evaluation Summary for Sequence Length 25:\n",
|
||||
"Users with >50% Accuracy: 16 / 32\n",
|
||||
"✅ Final Success Rate: 50.00%\n",
|
||||
"\n",
|
||||
"✅ All evaluations completed. Results saved to: /kaggle/working/evaluation_results.xlsx\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from pandas import ExcelWriter\n",
|
||||
"\n",
|
||||
"# === Run evaluation for each trained sequence length ===\n",
|
||||
"test_scenario = get_user_input_for_test()\n",
|
||||
"test_data = filter_test_data(df, test_scenario)\n",
|
||||
"\n",
|
||||
"output_excel_path = \"/kaggle/working/evaluation_results.xlsx\"\n",
|
||||
"\n",
|
||||
"with ExcelWriter(output_excel_path) as writer:\n",
|
||||
" for sequence_length, result in best_models.items():\n",
|
||||
" print(f\"\\n🔍 Testing Model for Sequence Length: {sequence_length}\")\n",
|
||||
" evaluate_model_on_test_data(\n",
|
||||
" result['model'],\n",
|
||||
" test_data.copy(),\n",
|
||||
" sequence_length,\n",
|
||||
" writer # 👈 pass the writer\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
"print(f\"\\n✅ All evaluations completed. Results saved to: {output_excel_path}\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"metadata": {
|
||||
"trusted": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"# # === Evaluation function (your version) ===\n",
|
||||
"# def evaluate_model_on_test_data(model, test_df, sequence_length):\n",
|
||||
"# print(\"\\n🧪 Evaluating on Test Data...\")\n",
|
||||
"# test_df = test_df.drop(columns=['Month', 'Year', 'date', 'DayOfWeek'])\n",
|
||||
"# test_df = test_df.sort_values(by='user').reset_index(drop=True)\n",
|
||||
"\n",
|
||||
"# users = test_df['user'].unique()\n",
|
||||
"# results = {}\n",
|
||||
"# accuracy_above_50 = 0\n",
|
||||
"\n",
|
||||
"# for user in users:\n",
|
||||
"# user_df = test_df[test_df['user'] == user]\n",
|
||||
"# X, y_true = [], []\n",
|
||||
"# user_features = user_df.drop(columns=['user']).values\n",
|
||||
"# user_labels = user_df['user'].values\n",
|
||||
"\n",
|
||||
"# if len(user_df) <= sequence_length:\n",
|
||||
"# print(f\"Skipping User {user} (not enough data for sequence length {sequence_length})\")\n",
|
||||
"# continue\n",
|
||||
"\n",
|
||||
"# for i in range(len(user_df) - sequence_length):\n",
|
||||
"# seq_x = user_features[i:i + sequence_length]\n",
|
||||
"# seq_y = user_labels[i + sequence_length]\n",
|
||||
"# X.append(seq_x)\n",
|
||||
"# y_true.append(seq_y)\n",
|
||||
"\n",
|
||||
"# X = np.array(X)\n",
|
||||
"# y_true = np.array(y_true)\n",
|
||||
"\n",
|
||||
"# if len(X) == 0:\n",
|
||||
"# continue\n",
|
||||
"\n",
|
||||
"# y_pred = model.predict(X, verbose=0)\n",
|
||||
"# y_pred_classes = np.argmax(y_pred, axis=1)\n",
|
||||
"\n",
|
||||
"# unique_pred, counts_pred = np.unique(y_pred_classes, return_counts=True)\n",
|
||||
"# label_counts_pred = dict(zip(unique_pred, counts_pred))\n",
|
||||
"\n",
|
||||
"# unique_true, counts_true = np.unique(y_true, return_counts=True)\n",
|
||||
"# label_counts_true = dict(zip(unique_true, counts_true))\n",
|
||||
"\n",
|
||||
"# acc = accuracy_score(y_true, y_pred_classes)\n",
|
||||
"# if acc > 0.5:\n",
|
||||
"# accuracy_above_50 += 1\n",
|
||||
"\n",
|
||||
"# results[user] = {\n",
|
||||
"# 'accuracy': acc,\n",
|
||||
"# 'predicted_counts': label_counts_pred,\n",
|
||||
"# 'actual_counts': label_counts_true\n",
|
||||
"# }\n",
|
||||
"\n",
|
||||
"# print(f\"\\n=== User {user} ===\")\n",
|
||||
"# print(f\"✅ Accuracy: {acc * 100:.2f}%\")\n",
|
||||
"# print(\"📊 Predicted Class Distribution:\", label_counts_pred)\n",
|
||||
"# print(\"📌 Actual Class Distribution: \", label_counts_true)\n",
|
||||
"\n",
|
||||
"# final_accuracy_percent = (accuracy_above_50 / 32) * 100\n",
|
||||
"# print(f\"\\n🟩 Final Evaluation Summary for Sequence Length {sequence_length}:\")\n",
|
||||
"# print(f\"Users with >50% Accuracy: {accuracy_above_50} / 32\")\n",
|
||||
"# print(f\"✅ Final Success Rate: {final_accuracy_percent:.2f}%\")\n",
|
||||
"\n",
|
||||
"# # === Run evaluation for each trained sequence length ===\n",
|
||||
"# test_scenario = get_user_input_for_test()\n",
|
||||
"# test_data = filter_test_data(df, test_scenario)\n",
|
||||
"\n",
|
||||
"# for sequence_length, result in best_models.items():\n",
|
||||
"# print(f\"\\n🔍 Testing Model for Sequence Length: {sequence_length}\")\n",
|
||||
"# evaluate_model_on_test_data(result['model'], test_data.copy(), sequence_length)\n",
|
||||
"\n",
|
||||
"# print(\"\\n✅ All evaluations completed.\")\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kaggle": {
|
||||
"accelerator": "nvidiaTeslaT4",
|
||||
"dataSources": [
|
||||
{
|
||||
"datasetId": 5775075,
|
||||
"sourceId": 9494285,
|
||||
"sourceType": "datasetVersion"
|
||||
}
|
||||
],
|
||||
"dockerImageVersionId": 31011,
|
||||
"isGpuEnabled": true,
|
||||
"isInternetEnabled": true,
|
||||
"language": "python",
|
||||
"sourceType": "notebook"
|
||||
},
|
||||
"kernelspec": {
|
||||
"display_name": ".venv",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.10.18"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
+848
@@ -0,0 +1,848 @@
|
||||
import json
|
||||
import os
|
||||
|
||||
import math
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import sklearn
|
||||
from keras.src.regularizers import L1L2
|
||||
from matplotlib import pyplot as plt
|
||||
from pandas import DataFrame
|
||||
from sklearn.calibration import CalibratedClassifierCV
|
||||
from sklearn.discriminant_analysis import QuadraticDiscriminantAnalysis, LinearDiscriminantAnalysis
|
||||
from sklearn.dummy import DummyClassifier
|
||||
from sklearn.ensemble import RandomForestClassifier, ExtraTreesClassifier, BaggingClassifier, VotingClassifier, \
|
||||
GradientBoostingClassifier, AdaBoostClassifier
|
||||
from sklearn.gaussian_process import GaussianProcessClassifier
|
||||
from sklearn.linear_model import PassiveAggressiveClassifier, RidgeClassifier, RidgeClassifierCV, SGDClassifier, \
|
||||
LogisticRegression, LogisticRegressionCV, Perceptron
|
||||
from sklearn.metrics import confusion_matrix
|
||||
from sklearn.mixture import GaussianMixture
|
||||
from sklearn.model_selection import GridSearchCV
|
||||
from sklearn.naive_bayes import GaussianNB, BernoulliNB, MultinomialNB
|
||||
from sklearn.neighbors import KNeighborsClassifier, NearestCentroid
|
||||
from sklearn.neural_network import MLPClassifier
|
||||
from sklearn.preprocessing import MinMaxScaler
|
||||
from sklearn.semi_supervised import LabelSpreading, LabelPropagation
|
||||
from sklearn.svm import LinearSVC, SVC, OneClassSVM
|
||||
from sklearn.tree import ExtraTreeClassifier, DecisionTreeClassifier
|
||||
|
||||
from pipeline_old import (
|
||||
load_dataset,
|
||||
filter_data,
|
||||
filter_test_data,
|
||||
prepare_user_data,
|
||||
train_models,
|
||||
evaluate_models,
|
||||
prepare_data_for_model, model_type_gru, model_type_lstm, model_type_bilstm, train_models_v2, train_one_model,
|
||||
eval_metrics, get_save_id, prepare_data_for_basic_algorithm, train_one_model_v2,
|
||||
)
|
||||
|
||||
year_str = 'Year'
|
||||
month_str = 'Month'
|
||||
day_str = 'Day'
|
||||
date_str = 'Date'
|
||||
time_str = 'Time'
|
||||
day_of_week_str = 'DayOfWeek'
|
||||
user_str = 'user'
|
||||
split_str = 'split type'
|
||||
data_split_str = 'data percentages'
|
||||
month_split_str = 'month percentages'
|
||||
threshold_str = 'threshold used'
|
||||
with_threshold_str = 'WITH'
|
||||
without_threshold_str = 'WITHOUT'
|
||||
timespan_str = 'time used'
|
||||
hour_timespan_str = '1HR'
|
||||
min_timespan_str = '15MIN'
|
||||
sequence_length_str = 'sequence length'
|
||||
accuracy_str = 'accuracy'
|
||||
precision_str = 'precision'
|
||||
recall_str = 'recall'
|
||||
f1_string = 'f1 score'
|
||||
model_type_str = 'model type'
|
||||
week_column_names = ['DayOfWeek_' + day for day in
|
||||
['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday' ]]
|
||||
figure_path = 'figures/'
|
||||
predicitons_path = 'preds/'
|
||||
|
||||
# === Configurable Parameters ===
|
||||
dataset_path = './Datasets/'
|
||||
dataset_hrs_path = './Datasets/hours.json'
|
||||
dataset_min_path = './Datasets/minutes.json'
|
||||
DATA_PATH = dataset_path +'ALLUSERS32_15MIN_WITHOUTTHREHOLD.xlsx'
|
||||
OUTPUT_EXCEL_PATH = './working/evaluation_results.xlsx'
|
||||
result_filename_v1 = './working/evaluation_results.json'
|
||||
result_filename_v2 = './working/evaluation_results_v2.json'
|
||||
SEQUENCE_LENGTHS = [30, 25, 20, 15, 10, 5] # You can add more: [20, 25, 30]
|
||||
|
||||
TRAINING_SCENARIO = [(2018, list(range(1, 13))), (2019, list(range(1, 10)))]
|
||||
VALIDATION_SCENARIO = [(2019, [10, 11, 12])]
|
||||
TEST_SCENARIO = [(2020, [1, 2])] # Jan–Feb 2020 only
|
||||
|
||||
# === Optional display only ===
|
||||
predefined_training_scenarios = {
|
||||
"Scenario 1": {"years_months": [(2018, list(range(1, 13))), (2019, list(range(1, 10)))]},
|
||||
"Scenario 2": {"years_months": [(2017, list(range(1, 13))), (2018, list(range(1, 13))), (2019, list(range(1, 10)))]}
|
||||
}
|
||||
predefined_validation_scenarios = {
|
||||
"Scenario A": {"years_months": [(2019, [10, 11, 12])]}
|
||||
}
|
||||
|
||||
def create_dir(path):
|
||||
"""
|
||||
Creates a directory if it doesn't exist yet.
|
||||
|
||||
:param path: The path to the directory
|
||||
"""
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
|
||||
def remove_covid_data(df):
|
||||
df = df[~(df[year_str]>=2020)]
|
||||
return df
|
||||
|
||||
def split_data_by_month_percentage(df, percentages):
|
||||
train_p, valid_p, test_p = percentages
|
||||
ids = df[[year_str, month_str]].drop_duplicates().sort_values([year_str, month_str])
|
||||
tr, va, te = np.split(ids, [int((train_p/100) * len(ids)), int(((train_p + valid_p)/100) * len(ids))])
|
||||
return df.merge(tr, on=[year_str, month_str], how='inner'), df.merge(va, on=[year_str, month_str], how='inner'), df.merge(te, on=[year_str, month_str], how='inner')
|
||||
|
||||
def split_data_by_userdata_percentage(df, percentages, sample=100):
|
||||
train_p, valid_p, test_p = percentages
|
||||
tr, va, te = pd.DataFrame(), pd.DataFrame(), pd.DataFrame()
|
||||
for user_id in df[user_str].unique():
|
||||
# !! following sample creates gaps in data if sample smaller 100
|
||||
user_data = df[df[user_str]==user_id].sample(frac=sample/ 100).sort_values([date_str]) # have to sort for time shift
|
||||
u_tr, u_va, u_te = np.split(user_data, [int((train_p/100)*len(user_data)), int(((train_p+valid_p)/100)*len(user_data))])
|
||||
tr = pd.concat([tr, u_tr], ignore_index=True)
|
||||
va = pd.concat([va, u_va], ignore_index=True)
|
||||
te = pd.concat([te, u_te], ignore_index=True)
|
||||
return tr, va, te
|
||||
|
||||
|
||||
def main():
|
||||
# print("=== Training Scenario Setup ===")
|
||||
# display_warning_about_2020_data()
|
||||
# display_warnings_for_scenarios("training", predefined_training_scenarios, predefined_validation_scenarios)
|
||||
|
||||
# print("\n=== Validation Scenario Setup ===")
|
||||
# display_warning_about_2020_data()
|
||||
# display_warnings_for_scenarios("validation", predefined_training_scenarios, predefined_validation_scenarios)
|
||||
|
||||
# === Load and preprocess ===
|
||||
df = load_dataset(DATA_PATH)
|
||||
|
||||
ALLUSERS32_15MIN_WITHOUTTHREHOLD = False
|
||||
if('ALLUSERS32_15MIN_WITHOUTTHREHOLD.xlsx' in DATA_PATH):
|
||||
ALLUSERS32_15MIN_WITHOUTTHREHOLD = True
|
||||
|
||||
training_data = filter_data(df, TRAINING_SCENARIO, ALLUSERS32_15MIN_WITHOUTTHREHOLD)
|
||||
validation_data = filter_data(df, VALIDATION_SCENARIO, ALLUSERS32_15MIN_WITHOUTTHREHOLD)
|
||||
|
||||
user_data_train = prepare_user_data(training_data)
|
||||
user_data_val = prepare_user_data(validation_data)
|
||||
|
||||
# === Train models ===
|
||||
best_models = train_models(user_data_train, user_data_val, sequence_lengths=SEQUENCE_LENGTHS)
|
||||
|
||||
# === Load and evaluate test ===
|
||||
test_df = filter_test_data(df, TEST_SCENARIO)
|
||||
evaluate_models(best_models, test_df, SEQUENCE_LENGTHS, OUTPUT_EXCEL_PATH, ALLUSERS32_15MIN_WITHOUTTHREHOLD)
|
||||
|
||||
print(f"\n✅ All evaluations completed. Results saved to: {OUTPUT_EXCEL_PATH}")
|
||||
|
||||
|
||||
def reduce_columns(df, filename):
|
||||
if min_timespan_str in filename:
|
||||
return df.drop(columns=['Month', 'Year', 'date', 'DayOfWeek'] + week_column_names, errors='ignore')
|
||||
else:
|
||||
return df.drop(columns=['Month', 'Year', 'date', 'DayOfWeek'], errors='ignore')
|
||||
|
||||
|
||||
def reduce_columns_v3(df):
|
||||
return df.drop(columns=[month_str, year_str, date_str])
|
||||
|
||||
def load_previous_results(filename):
|
||||
results = pd.DataFrame()
|
||||
if os.path.exists(filename):
|
||||
results = pd.DataFrame(json.load(open(filename)))
|
||||
return results
|
||||
|
||||
def main_two_v2(model_type):
|
||||
seq_length = range(10,31, 5)
|
||||
for sequence_length in seq_length:
|
||||
for data_filename in os.listdir(dataset_path):
|
||||
timespan_id = hour_timespan_str
|
||||
threshold_id = with_threshold_str
|
||||
if min_timespan_str in data_filename:
|
||||
timespan_id = min_timespan_str
|
||||
if without_threshold_str in data_filename:
|
||||
threshold_id = without_threshold_str
|
||||
|
||||
results = load_previous_results(result_filename_v2)
|
||||
if len(results) > 0:
|
||||
if len(results[(results[timespan_str]==timespan_id) &
|
||||
(results[threshold_str]==threshold_id) &
|
||||
(results[sequence_length_str]==sequence_length) &
|
||||
(results[model_type_str]==model_type)]) > 0:
|
||||
continue
|
||||
|
||||
file_path = os.path.join(dataset_path, data_filename)
|
||||
df = load_dataset(file_path)
|
||||
df = remove_covid_data(df)
|
||||
|
||||
tr,val,te = split_data_by_userdata_percentage(df, percentages=(80,10,10))
|
||||
tr = reduce_columns(tr, data_filename)
|
||||
val = reduce_columns(val, data_filename)
|
||||
te = reduce_columns(te, data_filename)
|
||||
|
||||
user_data_train = prepare_user_data(tr)
|
||||
user_data_val = prepare_user_data(val)
|
||||
|
||||
best_model = train_models_v2(user_data_train, user_data_val,
|
||||
sequence_length=sequence_length,
|
||||
model_type=model_type)
|
||||
|
||||
results = load_previous_results(result_filename_v2)
|
||||
results = pd.concat([results,
|
||||
evaluate_model_on_test_data(model=best_model,
|
||||
test_df=te,
|
||||
sequence_length=sequence_length,
|
||||
time_span_id=timespan_id,
|
||||
threshold_id=threshold_id,
|
||||
model_type=model_type,
|
||||
split_id=data_split_str)],
|
||||
ignore_index=True)
|
||||
results.to_json(result_filename_v2)
|
||||
|
||||
def main_two_v1():
|
||||
seq_length = [30, 25, 20, 15, 10, 5] # You can add more: [20, 25, 30]
|
||||
results = pd.DataFrame()
|
||||
if os.path.exists(result_filename_v1):
|
||||
results = pd.DataFrame(json.load(open(result_filename_v1)))
|
||||
for sequence_length in seq_length:
|
||||
for data_filename in os.listdir(dataset_path):
|
||||
for split_id, split_method in [(data_split_str, split_data_by_userdata_percentage),(month_split_str, split_data_by_month_percentage)]:
|
||||
for model_type in [model_type_lstm, model_type_bilstm, model_type_gru]:
|
||||
timespan_id = hour_timespan_str
|
||||
threshold_id = with_threshold_str
|
||||
if min_timespan_str in data_filename:
|
||||
timespan_id = min_timespan_str
|
||||
if without_threshold_str in data_filename:
|
||||
threshold_id = without_threshold_str
|
||||
if len(results) > 0:
|
||||
if len(results[(results[split_str]==split_id) &
|
||||
(results[timespan_str]==timespan_id) &
|
||||
(results[threshold_str]==threshold_id) &
|
||||
(results[sequence_length_str]==sequence_length) &
|
||||
(results[model_type_str]==model_type)]) > 0:
|
||||
continue
|
||||
|
||||
file_path = os.path.join(dataset_path, data_filename)
|
||||
df = load_dataset(file_path)
|
||||
df = remove_covid_data(df)
|
||||
tr,val,te = split_method(df, percentages=(80,10,10))
|
||||
tr = reduce_columns(tr, data_filename)
|
||||
val = reduce_columns(val, data_filename)
|
||||
te = reduce_columns(te, data_filename)
|
||||
|
||||
user_data_train = prepare_user_data(tr)
|
||||
user_data_val = prepare_user_data(val)
|
||||
|
||||
best_models = train_models(user_data_train, user_data_val, sequence_lengths=[sequence_length], model_type=model_type)
|
||||
|
||||
results = pd.concat([results,
|
||||
evaluate_model_on_test_data(model=best_models[sequence_length]['model'],
|
||||
test_df=te, split_id=split_id,
|
||||
sequence_length=sequence_length,
|
||||
time_span_id=timespan_id,
|
||||
threshold_id=threshold_id,
|
||||
model_type=model_type)], ignore_index=True)
|
||||
results.to_json(result_filename_v1)
|
||||
|
||||
# === Evaluation ===
|
||||
def evaluate_model_on_test_data(model, test_df,sequence_length, split_id, threshold_id, time_span_id, model_type):
|
||||
user_data = prepare_user_data(test_df)
|
||||
x, y = prepare_data_for_model(user_data=user_data, sequence_length=sequence_length)
|
||||
|
||||
y_pred = model.predict(x, verbose=0)
|
||||
y_pred_classes = np.argmax(y_pred, axis=1)
|
||||
|
||||
recall = sklearn.metrics.recall_score(y, y_pred_classes, average='weighted')
|
||||
precision = sklearn.metrics.precision_score(y, y_pred_classes, average='weighted')
|
||||
f1_score = sklearn.metrics.f1_score(y, y_pred_classes, average='weighted')
|
||||
return pd.DataFrame({split_str:[split_id], threshold_str:[threshold_id], timespan_str:[time_span_id],
|
||||
sequence_length_str:[sequence_length],
|
||||
model_type_str:[model_type], recall_str:[recall],
|
||||
precision_str:[precision], f1_string:[f1_score]})
|
||||
|
||||
def visualise_results_v1():
|
||||
results = pd.DataFrame(json.load(open(result_filename_v1)))
|
||||
# Month split ist immer schlechter
|
||||
results = results[results[split_str] == data_split_str]
|
||||
with_threshold = results[results[threshold_str] == with_threshold_str]
|
||||
without_threshold = results[results[threshold_str] == without_threshold_str]
|
||||
fig, axes = plt.subplots(2, 3)
|
||||
ax_col_id = 0
|
||||
ax_row_id = -1
|
||||
for timespan in [hour_timespan_str,min_timespan_str]:
|
||||
ax_row_id +=1
|
||||
for model in [model_type_lstm, model_type_bilstm, model_type_gru]:
|
||||
with_sub = with_threshold[(with_threshold[timespan_str] == timespan) & (with_threshold[model_type_str] == model)]
|
||||
without_sub = without_threshold[(without_threshold[timespan_str] == timespan) & (without_threshold[model_type_str] == model)]
|
||||
ax = axes[ax_row_id, ax_col_id]
|
||||
ax.set_title(model+' '+timespan)
|
||||
ax.plot(with_sub[sequence_length_str], with_sub[f1_string], label=with_threshold_str)
|
||||
ax.plot(without_sub[sequence_length_str], without_sub[f1_string], label=without_threshold_str)
|
||||
ax.legend()
|
||||
ax_col_id +=1
|
||||
ax_col_id %= 3
|
||||
fig.tight_layout()
|
||||
fig.savefig(figure_path+'v1_results.svg')
|
||||
# Fazit: keine eindeutig besseren Versionen erkennbar
|
||||
|
||||
|
||||
def visualise_results_v2():
|
||||
results = pd.DataFrame(json.load(open(result_filename_v2)))
|
||||
with_threshold = results[results[threshold_str] == with_threshold_str]
|
||||
without_threshold = results[results[threshold_str] == without_threshold_str]
|
||||
fig, axes = plt.subplots(2, 3)
|
||||
ax_col_id = 0
|
||||
ax_row_id = -1
|
||||
for timespan in [hour_timespan_str,min_timespan_str]:
|
||||
ax_row_id +=1
|
||||
for model in [model_type_lstm, model_type_bilstm, model_type_gru]:
|
||||
with_sub = with_threshold[(with_threshold[timespan_str] == timespan) & (with_threshold[model_type_str] == model)]
|
||||
without_sub = without_threshold[(without_threshold[timespan_str] == timespan) & (without_threshold[model_type_str] == model)]
|
||||
with_sub = with_sub.sort_values(sequence_length_str)
|
||||
without_sub = without_sub.sort_values(sequence_length_str)
|
||||
ax = axes[ax_row_id, ax_col_id]
|
||||
ax.set_title(model+' '+timespan)
|
||||
ax.plot(with_sub[sequence_length_str], with_sub[f1_string], label=with_threshold_str)
|
||||
ax.plot(without_sub[sequence_length_str], without_sub[f1_string], label=without_threshold_str)
|
||||
ax.legend()
|
||||
ax_col_id +=1
|
||||
ax_col_id %= 3
|
||||
fig.tight_layout()
|
||||
fig.savefig(figure_path+'v2_results.svg')
|
||||
# Fazit: keine eindeutig besseren Versionen erkennbar
|
||||
|
||||
|
||||
def test(model_type):
|
||||
sequence_length = 20
|
||||
data_filename = os.listdir(dataset_path)[0]
|
||||
timespan_id = hour_timespan_str
|
||||
threshold_id = with_threshold_str
|
||||
|
||||
file_path = os.path.join(dataset_path, data_filename)
|
||||
df = load_dataset(file_path)
|
||||
df = remove_covid_data(df)
|
||||
results = pd.DataFrame()
|
||||
|
||||
for percentage in [33,66,100]:
|
||||
print('Percentage:', percentage)
|
||||
tr,val,te = split_data_by_userdata_percentage(df, percentages=(80,10,10),sample=percentage)
|
||||
tr = reduce_columns(tr, data_filename)
|
||||
val = reduce_columns(val, data_filename)
|
||||
te = reduce_columns(te, data_filename)
|
||||
|
||||
user_data_train = prepare_user_data(tr)
|
||||
user_data_val = prepare_user_data(val)
|
||||
|
||||
best_model = train_models_v2(user_data_train, user_data_val,
|
||||
sequence_length=sequence_length,
|
||||
model_type=model_type)
|
||||
|
||||
results = pd.concat([results,
|
||||
evaluate_model_on_test_data(model=best_model,
|
||||
test_df=te,
|
||||
sequence_length=sequence_length,
|
||||
time_span_id=timespan_id,
|
||||
threshold_id=threshold_id,
|
||||
model_type=model_type,
|
||||
split_id=data_split_str)],
|
||||
ignore_index=True)
|
||||
print(results)
|
||||
|
||||
def manual_tuning(model_type):
|
||||
# load dataset
|
||||
sequence_length = 20
|
||||
data_filename = 'ALL32USERS15MIN_WITHTHRESHOLD.xlsx'
|
||||
timespan_id = min_timespan_str
|
||||
threshold_id = with_threshold_str
|
||||
|
||||
file_path = os.path.join(dataset_path, data_filename)
|
||||
df = load_dataset(file_path)
|
||||
df = remove_covid_data(df)
|
||||
|
||||
tr, val, te = split_data_by_userdata_percentage(df, percentages=(80, 10, 10), sample=100)
|
||||
tr = reduce_columns(tr, data_filename)
|
||||
val = reduce_columns(val, data_filename)
|
||||
te = reduce_columns(te, data_filename)
|
||||
|
||||
user_data_train = prepare_user_data(tr)
|
||||
user_data_val = prepare_user_data(val)
|
||||
|
||||
# fit and evaluate model
|
||||
# config
|
||||
repeats = 3
|
||||
n_batch = 1024
|
||||
n_epochs = 500
|
||||
n_neurons = 16
|
||||
l_rate = 1e-4
|
||||
reg = L1L2(l1=0.0, l2=0.0)
|
||||
|
||||
history_list = list()
|
||||
# run diagnostic tests
|
||||
for i in range(repeats):
|
||||
history = train_one_model(user_data_train, user_data_val, n_batch, n_epochs,
|
||||
n_neurons, l_rate, reg,
|
||||
sequence_length=sequence_length,
|
||||
model_type=model_type)
|
||||
history_list.append(history)
|
||||
for metric in ['p', 'r', 'f1']:
|
||||
for history in history_list:
|
||||
plt.plot(history['train_'+metric], color='blue')
|
||||
plt.plot(history['test_'+metric], color='orange')
|
||||
plt.savefig(figure_path+metric+'_e'+str(n_epochs)+'_n'+str(n_neurons)+'_b'+
|
||||
str(n_batch)+'_l'+str(l_rate)+'_diagnostic.png')
|
||||
plt.clf()
|
||||
print('Done')
|
||||
|
||||
|
||||
def upsampling(df):
|
||||
max_user_data = df[user_str].value_counts().max()
|
||||
for user in df[user_str].unique():
|
||||
user_data = df[df[user_str]==user]
|
||||
user_count = user_data.shape[0]
|
||||
times = max_user_data / user_count
|
||||
before_comma = math.floor(times)
|
||||
after_comma = times % 1
|
||||
after_comma_data = user_data.sample(frac=after_comma)
|
||||
for i in range(1, before_comma):
|
||||
df = pd.concat([df, user_data], ignore_index=True)
|
||||
df = pd.concat([df, after_comma_data], ignore_index=True)
|
||||
return df
|
||||
|
||||
|
||||
def manual_tuning_v3(model_type):
|
||||
# TODO: hrs/min
|
||||
sequence_length = 1
|
||||
|
||||
tr, val, te = get_prepared_data_v3(dataset_hrs_path)
|
||||
|
||||
# fit and evaluate model
|
||||
# config
|
||||
repeats = 3
|
||||
n_batch = 1024
|
||||
n_epochs = 10
|
||||
n_neurons = 256
|
||||
n_neurons2 = 512
|
||||
n_neurons3 = 512
|
||||
n_neurons4 = 128
|
||||
l_rate = 1e-2
|
||||
d1 = 256
|
||||
reg1 = L1L2(l1=0.0, l2=0.001)
|
||||
r1 = '0001'
|
||||
reg2 = L1L2(l1=0.0, l2=0.1)
|
||||
r2 = '01'
|
||||
|
||||
history_list = list()
|
||||
# run diagnostic tests
|
||||
for i in range(repeats):
|
||||
history = train_one_model(tr, val, n_batch, n_epochs,
|
||||
n_neurons,n_neurons2, n_neurons3, n_neurons4, l_rate, d1, r1, reg1, r2, reg2,
|
||||
sequence_length=sequence_length,
|
||||
model_type=model_type)
|
||||
history_list.append(history)
|
||||
for metric in ['acc', 'p', 'r', 'f1']:
|
||||
for history in history_list:
|
||||
plt.plot(history['train_'+metric], color='blue')
|
||||
plt.plot(history['test_'+metric], color='orange')
|
||||
plt.savefig(figure_path+'v3/'+metric+get_save_id(n_epochs, n_neurons, n_neurons2, n_neurons3,n_neurons4, n_batch, l_rate, d1, r1, r2)
|
||||
+'.png')
|
||||
plt.clf()
|
||||
print('Done')
|
||||
|
||||
|
||||
|
||||
def calculate_baselines():
|
||||
file_combinations = [(hour_timespan_str, with_threshold_str,'ALL32USERS1HR_WITHTHRESHOLD.xlsx'),
|
||||
(min_timespan_str, with_threshold_str, 'ALL32USERS15MIN_WITHTHRESHOLD.xlsx'),
|
||||
(min_timespan_str, without_threshold_str, 'ALLUSERS32_15MIN_WITHOUTTHREHOLD.xlsx'),
|
||||
(hour_timespan_str, without_threshold_str, 'ALLUSERS_32_1HR_WITHOUT_THRESHOLD.xlsx'),
|
||||
]
|
||||
baseline_res = pd.DataFrame()
|
||||
for timespan_id, threshold_id, filename in file_combinations:
|
||||
file_path = os.path.join(dataset_path, filename)
|
||||
df = load_dataset(file_path)
|
||||
df = remove_covid_data(df)
|
||||
|
||||
_, _, te = split_data_by_userdata_percentage(df, percentages=(80, 10, 10), sample=20)
|
||||
te = reduce_columns(te, filename)
|
||||
user_data_te = prepare_user_data(te)
|
||||
for sequence_length in range(5,30, 5):
|
||||
x, y = prepare_data_for_model(user_data=user_data_te, sequence_length=sequence_length)
|
||||
|
||||
for strategy in ['most_frequent', 'stratified', 'uniform']:
|
||||
cls = DummyClassifier(strategy=strategy)
|
||||
cls.fit(x,y)
|
||||
y_pred = cls.predict(x)
|
||||
acc, p, r, f1 = eval_metrics(y_true=y, y_pred=y_pred)
|
||||
baseline_res = pd.concat([baseline_res,
|
||||
DataFrame({ 'strategy':[strategy], threshold_str:[threshold_id],
|
||||
timespan_str:[timespan_id], sequence_length_str:[sequence_length],
|
||||
accuracy_str:[acc],precision_str:[p],recall_str:[r],
|
||||
f1_string:f1})], ignore_index=True)
|
||||
baseline_res.to_json('baseline_results.json')
|
||||
print('Done')
|
||||
|
||||
def get_prepared_data_v3(filename, sample=100, print_unique=False):
|
||||
df = pd.read_json(filename)
|
||||
df = remove_covid_data(df)
|
||||
|
||||
# remove users which are a complete subset of another user (but keep one)
|
||||
users_to_remove = []
|
||||
for user_a in df[user_str].unique():
|
||||
for user_b in df[user_str].unique():
|
||||
if user_a != user_b:
|
||||
data = pd.concat([df[df[user_str]==user_a], df[df[user_str]==user_b]])
|
||||
columns = data.columns.tolist()
|
||||
columns.remove(user_str)
|
||||
|
||||
no_dup = data.drop_duplicates(columns, keep=False)
|
||||
if len(no_dup[no_dup[user_str]==user_a]) == 0:
|
||||
if print_unique:
|
||||
print(user_a, 'is subset of',user_b)
|
||||
if user_b not in users_to_remove:
|
||||
users_to_remove.append(user_a)
|
||||
df = df[~df[user_str].isin(users_to_remove)]
|
||||
|
||||
# bin steps per hour TODO: adjust for minutes
|
||||
for hour in ['Hour_'+str(i) for i in range(24)]:
|
||||
hour_data = df[hour]
|
||||
# smaller 1000 - round to 10
|
||||
a = ((hour_data[hour_data<1000]/10).round()*10)
|
||||
# between 1000 and 10000 - round to next 100
|
||||
b = ((hour_data[(hour_data>=1000)& (hour_data<10000)]/100).round()*100)
|
||||
# higher or equal 10000 - one class
|
||||
c = hour_data[hour_data > 10000]
|
||||
c = pd.Series(data={ind:10000 for ind in c.index}, index=c.index)
|
||||
new = pd.concat([a, b, c]).sort_index().astype(int)
|
||||
df[hour] = new
|
||||
|
||||
# remove users with too little data (optional)
|
||||
#value_counts = df[user_str].value_counts()
|
||||
#df = df[df[user_str].isin(value_counts[value_counts>200].index)]
|
||||
min_datapoints = 500 # 500 leads to at least 75 datapoints in the valid set
|
||||
users_to_remove = set()
|
||||
cols = df.columns.tolist()
|
||||
cols.remove(user_str)
|
||||
reduced = df.drop_duplicates(subset=cols, keep=False)
|
||||
for user_id in df[user_str].unique():
|
||||
subset = df[df[user_str] == user_id]
|
||||
reduced_subset = reduced[reduced[user_str] == user_id]
|
||||
if print_unique:
|
||||
print(user_id, len(subset), len(reduced_subset))
|
||||
if len(reduced_subset) < min_datapoints:
|
||||
users_to_remove.add(user_id)
|
||||
if print_unique:
|
||||
print('removing', user_id)
|
||||
|
||||
df = df[~df[user_str].isin(users_to_remove)]
|
||||
|
||||
tr, val, te = split_data_by_userdata_percentage(df, percentages=(70, 15, 15), sample=sample)
|
||||
tr = reduce_columns_v3(tr)
|
||||
val = reduce_columns_v3(val)
|
||||
te = reduce_columns_v3(te)
|
||||
|
||||
|
||||
|
||||
print('Train: Users', len(tr[user_str].unique()), 'mean num datapoins:', tr[user_str].value_counts().mean())
|
||||
print('Valid: Users', len(val[user_str].unique()), 'mean num datapoins:', val[user_str].value_counts().mean())
|
||||
print('Test: Users', len(te[user_str].unique()), 'mean num datapoins:', te[user_str].value_counts().mean())
|
||||
|
||||
tr, val, te = add_features(tr), add_features(val), add_features(te)
|
||||
|
||||
scaler = MinMaxScaler()
|
||||
scaler.fit(tr.drop(columns=[user_str]))
|
||||
|
||||
return scale_dataset(scaler, tr), scale_dataset(scaler, val), scale_dataset(scaler, te)
|
||||
|
||||
|
||||
def scale_dataset(scaler, df):
|
||||
y = df[user_str]
|
||||
x_scaled = scaler.transform(df.drop(columns=[user_str]))
|
||||
x_scaled = pd.DataFrame(x_scaled)
|
||||
x_scaled.columns = df.drop(columns=[user_str]).columns
|
||||
|
||||
df_scaled = pd.concat([x_scaled, pd.DataFrame(y.reset_index()[user_str])], axis=1)
|
||||
# df_scaled.columns = df.columns
|
||||
return prepare_user_data(df_scaled)
|
||||
|
||||
|
||||
def calculate_baselines_v3():
|
||||
file_combinations = [(hour_timespan_str, dataset_hrs_path),
|
||||
# (min_timespan_str, dataset_min_path), # TODO: dataset bining not ready for minutes
|
||||
]
|
||||
baseline_res = pd.DataFrame()
|
||||
for timespan_id, filename in file_combinations:
|
||||
_, _, te = get_prepared_data_v3(filename)
|
||||
for sequence_length in range(1,30,5):
|
||||
x, y = prepare_data_for_model(user_data=te, sequence_length=sequence_length)
|
||||
|
||||
for strategy in ['most_frequent', 'stratified', 'uniform']:
|
||||
cls = DummyClassifier(strategy=strategy)
|
||||
cls.fit(x,y)
|
||||
y_pred = cls.predict(x)
|
||||
acc, p, r, f1 = eval_metrics(y_true=y, y_pred=y_pred)
|
||||
baseline_res = pd.concat([baseline_res,
|
||||
DataFrame({ 'strategy':[strategy],
|
||||
timespan_str:[timespan_id], sequence_length_str:[sequence_length],
|
||||
accuracy_str:[acc],precision_str:[p],recall_str:[r],
|
||||
f1_string:f1})], ignore_index=True)
|
||||
baseline_res.to_json('baseline_results_v3.json')
|
||||
print('Done')
|
||||
|
||||
|
||||
def hypertune_basic_algorithms():
|
||||
# TODO: hrs/min
|
||||
# iterate over sequence lengths
|
||||
sequence_length = 7
|
||||
|
||||
tr, val, te = get_prepared_data_v3(dataset_hrs_path)
|
||||
|
||||
x_tr, y_tr = prepare_data_for_basic_algorithm(user_data=tr, sequence_length=sequence_length)
|
||||
x_val, y_val = prepare_data_for_basic_algorithm(user_data=val, sequence_length=sequence_length)
|
||||
|
||||
random_state = 17
|
||||
results = pd.DataFrame()
|
||||
for tag, clf, grid in [
|
||||
('GradientBoosting', GradientBoostingClassifier(random_state=random_state),
|
||||
{'loss': ['log_loss', 'exponential'],
|
||||
'learning_rate': [0.1, 0.5, 1.0,2.0, 5.0],
|
||||
'n_estimators': [10, 50, 100, 150, 200],
|
||||
'subsample': [0.1, 0.5, 1.0],
|
||||
'criterion': ['friedman_mse', 'squared_error'],
|
||||
'min_samples_split': [2, 10, 100],
|
||||
'min_samples_leaf': [1, 5, 10],
|
||||
'min_weight_fraction_leaf': [0.0, 0.1, 0.5],
|
||||
'max_depth': [None, 2, 10, 100],
|
||||
'min_impurity_decrease': [0.0, 0.1, 0.5],
|
||||
'max_features': ['sqrt', 'log2', None, 10, 20],
|
||||
'max_leaf_nodes': [None, 1, 5, 10],
|
||||
}),
|
||||
('Bernoulli', BernoulliNB(), {'fit_prior': [True, False],
|
||||
'binarize': [0.0, 0.1, 0.25, 0.5, 0.75],
|
||||
'force_alpha': [True, False],
|
||||
'alpha':[0.0, 0.25, 0.5, 0.75, 1.0]}),
|
||||
('extra trees', ExtraTreesClassifier(random_state=random_state, n_jobs=1),
|
||||
{'n_estimators': [10, 50, 100, 150, 200],
|
||||
'criterion': ['gini', 'entropy', 'log_loss'],
|
||||
'max_depth': [None, 2, 10, 100],
|
||||
'min_samples_split': [2, 10, 100],
|
||||
'min_samples_leaf': [1, 5, 10],
|
||||
'min_weight_fraction_leaf': [0.0, 0.1, 0.5],
|
||||
'max_features': ['sqrt', 'log2', None, 10, 20],
|
||||
'max_leaf_nodes': [None, 1, 5, 10],
|
||||
'min_impurity_decrease': [0.0, 0.1, 0.5],
|
||||
'bootstrap': [True, False],
|
||||
'class_weight': [None, 'balanced', 'balanced_subsample'],
|
||||
'max_samples': [None, 0.1, 0.2, 0.3]}
|
||||
),
|
||||
('random forest', RandomForestClassifier(random_state=random_state, n_jobs=1),
|
||||
{'n_estimators':[10, 50, 100, 150, 200],
|
||||
'criterion':['gini', 'entropy', 'log_loss'],
|
||||
'max_depth':[None, 2, 10,100],
|
||||
'min_samples_split': [2,10,100],
|
||||
'min_samples_leaf':[1,5,10],
|
||||
'min_weight_fraction_leaf':[0.0,0.1, 0.5],
|
||||
'max_features':['sqrt', 'log2', None, 10, 20],
|
||||
'max_leaf_nodes':[None, 1, 5, 10],
|
||||
'min_impurity_decrease':[0.0, 0.1, 0.5],
|
||||
'bootstrap':[True, False],
|
||||
'class_weight':[None, 'balanced', 'balanced_subsample'],
|
||||
'max_samples':[None,0.1, 0.2, 0.3]})
|
||||
]:
|
||||
grid_search = GridSearchCV(
|
||||
estimator=clf, param_grid=grid, scoring='f1_weighted', cv=5, n_jobs=1)
|
||||
grid_search.fit(x_tr, y_tr)
|
||||
|
||||
best_model = grid_search.best_estimator_
|
||||
y_pred = best_model.predict(x_val)
|
||||
acc, p, r, f1 = eval_metrics(y_true=y_val, y_pred=y_pred)
|
||||
results = pd.concat([results, DataFrame({ 'params': str(grid_search.best_params_),
|
||||
'tag':tag,accuracy_str:[acc],precision_str:[p],recall_str:[r],f1_string:f1})], ignore_index=True)
|
||||
results.to_json('basic_ht_results.json')
|
||||
print('Done')
|
||||
|
||||
def test_basic_algorithms():
|
||||
# TODO: hrs/min
|
||||
# TODO: iterate over sequence lengths
|
||||
sequence_length = 21
|
||||
|
||||
tr, val, te = get_prepared_data_v3(dataset_hrs_path)
|
||||
|
||||
x_tr, y_tr = prepare_data_for_basic_algorithm(user_data=tr, sequence_length=sequence_length)
|
||||
x_val, y_val = prepare_data_for_basic_algorithm(user_data=val, sequence_length=sequence_length)
|
||||
|
||||
random_state = 17
|
||||
results = pd.DataFrame()
|
||||
for tag, clf in [
|
||||
# ('Label Propagation', LabelPropagation()),
|
||||
# ('Label Spreading', LabelSpreading()),
|
||||
# ('VBGMM', GaussianMixture(random_state=random_state)),
|
||||
# ('linear discrimenant analysis', LinearDiscriminantAnalysis()),
|
||||
# ('discriminent analysis', QuadraticDiscriminantAnalysis()),
|
||||
# ('oneclassSVM', OneClassSVM()),
|
||||
# ('mlp', MLPClassifier(random_state=random_state)),
|
||||
# ('Perceptron', Perceptron(random_state=random_state)),
|
||||
# ('SVC', SVC(random_state=random_state)),
|
||||
#('logisticRegression', LogisticRegression(random_state=random_state)),
|
||||
#('logisticRegressionCV', LogisticRegressionCV(random_state=random_state)),
|
||||
#('multinomialNB', MultinomialNB()),
|
||||
#('nearestCentroid', NearestCentroid()),
|
||||
#('linearSVC', LinearSVC(random_state=random_state)),
|
||||
#('ada boost', AdaBoostClassifier(random_state=random_state)),
|
||||
#('GradientBoosting', GradientBoostingClassifier(random_state=random_state)),
|
||||
#('Bernoulli', BernoulliNB()),
|
||||
#('claibrated', CalibratedClassifierCV()),
|
||||
#('naive Bayes', GaussianNB()),
|
||||
#('sgd', SGDClassifier(random_state=random_state)),
|
||||
#('ridgeCV', RidgeClassifierCV()),
|
||||
# ('ridge', RidgeClassifier(random_state=random_state)),
|
||||
# ('passiveAggressive', PassiveAggressiveClassifier(random_state=random_state)),
|
||||
# ('knn', KNeighborsClassifier()),
|
||||
# ('bagging', BaggingClassifier(random_state=random_state)),
|
||||
# ('decision tree', DecisionTreeClassifier(random_state=random_state)),
|
||||
# ('extra tree', ExtraTreeClassifier(random_state=random_state)),
|
||||
# ('extra trees', ExtraTreesClassifier(random_state=random_state)),
|
||||
('random forest', RandomForestClassifier(random_state=random_state))
|
||||
]:
|
||||
clf.fit(x_tr, y_tr)
|
||||
y_pred = clf.predict(x_val)
|
||||
acc, p, r, f1 = eval_metrics(y_true=y_val, y_pred=y_pred)
|
||||
results = pd.concat([results, DataFrame({ 'tag':tag,accuracy_str:[acc],precision_str:[p],recall_str:[r],f1_string:f1})], ignore_index=True)
|
||||
print('Done')
|
||||
|
||||
|
||||
def add_features(df):
|
||||
# indicator weekend
|
||||
df['weekend'] = df[day_of_week_str + '_Saturday']+df[day_of_week_str + '_Sunday']
|
||||
# sum of steps per day
|
||||
df['day_total'] = sum([df['Hour_'+str(i)] for i in range(23)])
|
||||
# sum of steps morning, afternoon, evening, night
|
||||
df['morning_total'] = sum([df['Hour_' + str(i)] for i in range(6,13)])
|
||||
df['afternoon_total'] = sum([df['Hour_' + str(i)] for i in range(13,19)])
|
||||
df['evening_total'] = sum([df['Hour_' + str(i)] for i in range(19,23)])
|
||||
df['night_total'] = sum([df['Hour_' + str(i)] for i in [23,0,1,2,3,4,5]])
|
||||
return df
|
||||
|
||||
|
||||
def feature_engineering():
|
||||
sequence_length = 1
|
||||
|
||||
tr, val, te = get_prepared_data_v3(dataset_hrs_path, print_unique=True)
|
||||
|
||||
x_tr, y_tr = prepare_data_for_basic_algorithm(user_data=tr, sequence_length=sequence_length)
|
||||
x_val, y_val = prepare_data_for_basic_algorithm(user_data=val, sequence_length=sequence_length)
|
||||
|
||||
random_state = 17
|
||||
clf=RandomForestClassifier(random_state=random_state)
|
||||
clf.fit(x_tr, y_tr)
|
||||
y_pred = clf.predict(x_val)
|
||||
acc, p, r, f1 = eval_metrics(y_true=y_val, y_pred=y_pred)
|
||||
cf = confusion_matrix(y_pred=y_pred, y_true=y_val)
|
||||
# TODO: welche funktionieren schlecht? warum?
|
||||
# TODO: auf minutes umändern
|
||||
|
||||
print('Done')
|
||||
|
||||
|
||||
def test_sequence_length_on_approach(clf = RandomForestClassifier(random_state=17)):
|
||||
tr, val, te = get_prepared_data_v3(dataset_hrs_path)
|
||||
|
||||
results_train = pd.DataFrame()
|
||||
results_valid = pd.DataFrame()
|
||||
for sequence_length in range(1, 60, 5):
|
||||
x_tr, y_tr = prepare_data_for_basic_algorithm(user_data=tr, sequence_length=sequence_length)
|
||||
x_val, y_val = prepare_data_for_basic_algorithm(user_data=val, sequence_length=sequence_length)
|
||||
clf.fit(x_tr, y_tr)
|
||||
acc, p, r, f1 = eval_metrics(y_true=y_val, y_pred=clf.predict(x_val))
|
||||
results_valid = pd.concat([results_valid, DataFrame({sequence_length_str:[sequence_length], accuracy_str:[acc],precision_str:[p],recall_str:[r],f1_string:f1})], ignore_index=True)
|
||||
acc, p, r, f1 = eval_metrics(y_true=y_tr, y_pred=clf.predict(x_tr))
|
||||
results_train = pd.concat([results_train, DataFrame({sequence_length_str:[sequence_length], accuracy_str:[acc],precision_str:[p],recall_str:[r],f1_string:f1})], ignore_index=True)
|
||||
|
||||
fig = plt.figure()
|
||||
|
||||
for frame in [results_train, results_valid]:
|
||||
plt.plot(frame[sequence_length_str], frame[f1_string])
|
||||
|
||||
plt.show()
|
||||
print('')
|
||||
|
||||
def manual_tuning_v4(model_type):
|
||||
# TODO: hrs/min
|
||||
tr, val, te = get_prepared_data_v3(dataset_hrs_path)
|
||||
n_epochs= 20
|
||||
n_neurons = 1024
|
||||
results_train = pd.DataFrame()
|
||||
results_valid = pd.DataFrame()
|
||||
for sequence_length in range(1, 50, 5):
|
||||
train_data = prepare_data_for_model(user_data=tr, sequence_length=sequence_length)
|
||||
val_data = prepare_data_for_model(user_data=val, sequence_length=sequence_length)
|
||||
|
||||
# fit and evaluate model
|
||||
history_list = list()
|
||||
repeats = 3
|
||||
# run diagnostic tests
|
||||
for i in range(repeats):
|
||||
history = train_one_model_v2(train_data, val_data, 1024, n_epochs,
|
||||
n_neurons, sequence_length=sequence_length,
|
||||
model_type=model_type)
|
||||
history_list.append(history)
|
||||
results = pd.concat([history.tail(1) for history in history_list]).mean()
|
||||
results_train = pd.concat([results_train,
|
||||
DataFrame({sequence_length_str:[sequence_length],
|
||||
accuracy_str:[results['train_acc']],
|
||||
precision_str:[results['train_p']],
|
||||
recall_str:[results['train_r']],
|
||||
f1_string:[results['train_f1']]})], ignore_index=True)
|
||||
results_valid = pd.concat([results_valid,
|
||||
DataFrame({sequence_length_str:[sequence_length],
|
||||
accuracy_str:[results['test_acc']],
|
||||
precision_str:[results['test_p']],
|
||||
recall_str:[results['test_r']],
|
||||
f1_string:[results['test_f1']]})], ignore_index=True)
|
||||
|
||||
fig = plt.figure()
|
||||
for frame in [results_train, results_valid]:
|
||||
plt.plot(frame[sequence_length_str], frame[f1_string])
|
||||
|
||||
plt.show()
|
||||
print('Done')
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Ordner erstellen, die benötigt werden
|
||||
create_dir('results/')
|
||||
create_dir(figure_path)
|
||||
pd.options.mode.copy_on_write = True
|
||||
|
||||
main_two_v1()
|
||||
visualise_results_v1()
|
||||
#test(model_type=model_type_gru)
|
||||
# main_two_v2(model_type=model_type_gru)
|
||||
#visualise_results_v2()
|
||||
#manual_tuning(model_type=model_type_lstm)
|
||||
#calculate_baselines()
|
||||
|
||||
#### Ab hier aktuell (21.01.2026)
|
||||
#calculate_baselines()
|
||||
# manual_tuning_v3(model_type=model_type_lstm)
|
||||
#test_basic_algorithms()
|
||||
# test_basic_algorithm_on_sequence_lengths()
|
||||
manual_tuning_v4(model_type=model_type_lstm)
|
||||
#feature_engineering()
|
||||
#hypertune_basic_algorithms()
|
||||
print('Done')
|
||||
@@ -0,0 +1,296 @@
|
||||
import numpy as np # linear algebra
|
||||
import pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)
|
||||
from pandas import ExcelWriter
|
||||
import shutil
|
||||
import os
|
||||
import keras_tuner as kt
|
||||
|
||||
from tensorflow.keras.models import Sequential
|
||||
from tensorflow.keras.layers import LSTM, Dense, Dropout,GRU,Bidirectional
|
||||
from tensorflow.keras.optimizers import Adam
|
||||
from tensorflow.keras.callbacks import ReduceLROnPlateau, EarlyStopping
|
||||
from keras_tuner import RandomSearch
|
||||
from sklearn.metrics import accuracy_score
|
||||
|
||||
# === Clean previous tuning directory ===
|
||||
shutil.rmtree("./working/tuner", ignore_errors=True)
|
||||
|
||||
# === Load dataset ===
|
||||
file_path = './Datasets/ALLUSERS32_15MIN_WITHOUTTHREHOLD.xlsx'
|
||||
|
||||
df = pd.read_excel(file_path)
|
||||
|
||||
# === Helper functions for scenario selection ===
|
||||
def get_user_input_for_scenario(scenario_type):
|
||||
print(f"\nPlease define your custom {scenario_type} scenario:")
|
||||
years_input = input(f"Enter {scenario_type} years (comma-separated, e.g., 2017,2018): ").strip()
|
||||
years = list(map(int, years_input.split(',')))
|
||||
years_months = []
|
||||
for year in years:
|
||||
months_input = input(f"Enter months for year {year} (comma-separated, e.g., 1,2,3): ").strip()
|
||||
months = list(map(int, months_input.split(',')))
|
||||
years_months.append((year, months))
|
||||
return years_months
|
||||
|
||||
def display_warning_about_2020_data():
|
||||
print("\n⚠️ Warning: 2020 data after February is excluded due to COVID-19.")
|
||||
print("✅ Only Jan and Feb 2020 are used for testing. Do not use them in training/validation.")
|
||||
|
||||
def display_warnings_for_scenarios(scenario_type):
|
||||
if scenario_type == "training":
|
||||
print("\n⚠️ Predefined Training Scenarios (for reference only):")
|
||||
for name, scenario in predefined_training_scenarios.items():
|
||||
parts = [f"{year}-{months}" for year, months in scenario['years_months']]
|
||||
print(f" {name}: {', '.join(parts)}")
|
||||
elif scenario_type == "validation":
|
||||
print("\n⚠️ Predefined Validation Scenario:")
|
||||
for name, scenario in predefined_validation_scenarios.items():
|
||||
parts = [f"{year}-{months}" for year, months in scenario['years_months']]
|
||||
print(f" {name}: {', '.join(parts)}")
|
||||
print(" - This uses Oct, Nov, Dec of 2019")
|
||||
|
||||
predefined_training_scenarios = {
|
||||
"Scenario 1": {"years_months": [(2018, list(range(1, 13))), (2019, list(range(1, 10)))]},
|
||||
"Scenario 2": {"years_months": [(2017, list(range(1, 13))), (2018, list(range(1, 13))), (2019, list(range(1, 10)))]}
|
||||
}
|
||||
predefined_validation_scenarios = {
|
||||
"Scenario A": {"years_months": [(2019, [10, 11, 12])]}
|
||||
}
|
||||
|
||||
# === Filter and preprocess data ===
|
||||
def filter_data(df, scenario):
|
||||
filtered = pd.DataFrame()
|
||||
for year, months in scenario:
|
||||
filtered = pd.concat([filtered, df[(df['Year'] == year) & (df['Month'].isin(months))]])
|
||||
return filtered.drop(columns=['Month', 'Year', 'date', 'DayOfWeek'])
|
||||
|
||||
# === Get test scenario input ===
|
||||
def get_user_input_for_test():
|
||||
print("\n=== Testing Scenario Setup ===")
|
||||
print("⚠️ Only January and February of 2020 were used for testing in predefined setup.")
|
||||
print("⚠️ Avoid using 2020 data after February due to COVID-19 impact.\n")
|
||||
years_input = input("Enter test years (comma-separated, e.g., 2020): ").strip()
|
||||
years = list(map(int, years_input.split(',')))
|
||||
years_months = []
|
||||
for year in years:
|
||||
months_input = input(f"Enter months for year {year} (comma-separated, e.g., 1,2): ").strip()
|
||||
months = list(map(int, months_input.split(',')))
|
||||
years_months.append((year, months))
|
||||
return years_months
|
||||
|
||||
def filter_test_data(df, scenario):
|
||||
data_parts = []
|
||||
for year, months in scenario:
|
||||
part = df[(df['Year'] == year) & (df['Month'].isin(months))]
|
||||
data_parts.append(part)
|
||||
return pd.concat(data_parts, ignore_index=True)
|
||||
|
||||
def evaluate_model_on_test_data(model, test_df, sequence_length, excel_writer):
|
||||
print("\n🧪 Evaluating on Test Data...")
|
||||
test_df = test_df.drop(columns=['Month', 'Year', 'date', 'DayOfWeek'])
|
||||
test_df = test_df.sort_values(by='user').reset_index(drop=True)
|
||||
|
||||
users = test_df['user'].unique()
|
||||
results = []
|
||||
accuracy_above_50 = 0
|
||||
|
||||
for user in users:
|
||||
user_df = test_df[test_df['user'] == user]
|
||||
X, y_true = [], []
|
||||
user_features = user_df.drop(columns=['user']).values
|
||||
user_labels = user_df['user'].values
|
||||
|
||||
if len(user_df) <= sequence_length:
|
||||
print(f"Skipping User {user} (not enough data for sequence length {sequence_length})")
|
||||
continue
|
||||
|
||||
for i in range(len(user_df) - sequence_length):
|
||||
seq_x = user_features[i:i + sequence_length]
|
||||
seq_y = user_labels[i + sequence_length]
|
||||
X.append(seq_x)
|
||||
y_true.append(seq_y)
|
||||
|
||||
X = np.array(X)
|
||||
y_true = np.array(y_true)
|
||||
|
||||
if len(X) == 0:
|
||||
continue
|
||||
|
||||
y_pred = model.predict(X, verbose=0)
|
||||
y_pred_classes = np.argmax(y_pred, axis=1)
|
||||
|
||||
unique_pred, counts_pred = np.unique(y_pred_classes, return_counts=True)
|
||||
label_counts_pred = dict(zip(unique_pred, counts_pred))
|
||||
|
||||
unique_true, counts_true = np.unique(y_true, return_counts=True)
|
||||
label_counts_true = dict(zip(unique_true, counts_true))
|
||||
|
||||
acc = accuracy_score(y_true, y_pred_classes)
|
||||
if acc > 0.5:
|
||||
accuracy_above_50 += 1
|
||||
|
||||
# Append result to list
|
||||
results.append({
|
||||
'User': user,
|
||||
'Accuracy (%)': acc * 100,
|
||||
'Predicted Class Distribution': str(label_counts_pred),
|
||||
'Actual Class Distribution': str(label_counts_true)
|
||||
})
|
||||
|
||||
print(f"\n=== User {user} ===")
|
||||
print(f"✅ Accuracy: {acc * 100:.2f}%")
|
||||
print("📊 Predicted Class Distribution:", label_counts_pred)
|
||||
print("📌 Actual Class Distribution: ", label_counts_true)
|
||||
|
||||
final_accuracy_percent = (accuracy_above_50 / 32) * 100
|
||||
print(f"\n🟩 Final Evaluation Summary for Sequence Length {sequence_length}:")
|
||||
print(f"Users with >50% Accuracy: {accuracy_above_50} / 32")
|
||||
print(f"✅ Final Success Rate: {final_accuracy_percent:.2f}%")
|
||||
|
||||
# Append overall stats as a new row
|
||||
results.append({
|
||||
'User': 'TOTAL',
|
||||
'Accuracy (%)': '',
|
||||
'Predicted Class Distribution': f'Users >50% Acc: {accuracy_above_50}/32',
|
||||
'Actual Class Distribution': f'Success Rate: {final_accuracy_percent:.2f}%'
|
||||
})
|
||||
|
||||
# Save results to Excel sheet
|
||||
df_results = pd.DataFrame(results)
|
||||
df_results.to_excel(excel_writer, sheet_name=f"SeqLen_{sequence_length}", index=False)
|
||||
|
||||
|
||||
# === Get user-defined training and validation scenarios ===
|
||||
print("=== Training Scenario Setup ===")
|
||||
display_warning_about_2020_data()
|
||||
display_warnings_for_scenarios("training")
|
||||
training_scenario = get_user_input_for_scenario("training")
|
||||
|
||||
print("\n=== Validation Scenario Setup ===")
|
||||
display_warning_about_2020_data()
|
||||
display_warnings_for_scenarios("validation")
|
||||
validation_scenario = get_user_input_for_scenario("validation")
|
||||
|
||||
data = filter_data(df, training_scenario)
|
||||
data_val = filter_data(df, validation_scenario)
|
||||
|
||||
# === Organize by user ===
|
||||
df_sorted = data.sort_values(by='user').reset_index(drop=True)
|
||||
df_sorted_val = data_val.sort_values(by='user').reset_index(drop=True)
|
||||
users = df_sorted['user'].unique()
|
||||
users_val = df_sorted_val['user'].unique()
|
||||
|
||||
user_data = {user: df_sorted[df_sorted['user'] == user] for user in users}
|
||||
user_data_val = {user: df_sorted_val[df_sorted_val['user'] == user] for user in users_val}
|
||||
|
||||
# === Callbacks ===
|
||||
early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)
|
||||
lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=5, verbose=1)
|
||||
|
||||
# === Model tuning and training loop ===
|
||||
best_models = {}
|
||||
|
||||
for sequence_length in range(20, 30, 5):
|
||||
print(f"\n=== Training for Sequence Length: {sequence_length} ===")
|
||||
|
||||
# Training data
|
||||
X, y = [], []
|
||||
for user, data in user_data.items():
|
||||
features = data.drop('user', axis=1).values
|
||||
labels = data['user'].values
|
||||
for i in range(len(features) - sequence_length):
|
||||
X.append(features[i:i + sequence_length])
|
||||
y.append(labels[i + sequence_length])
|
||||
X = np.array(X)
|
||||
y = np.array(y)
|
||||
|
||||
# Validation data
|
||||
X_val, y_val = [], []
|
||||
for user, data in user_data_val.items():
|
||||
features = data.drop('user', axis=1).values
|
||||
labels = data['user'].values
|
||||
for i in range(len(features) - sequence_length):
|
||||
X_val.append(features[i:i + sequence_length])
|
||||
y_val.append(labels[i + sequence_length])
|
||||
X_val = np.array(X_val)
|
||||
y_val = np.array(y_val)
|
||||
|
||||
if X.shape[0] == 0 or X_val.shape[0] == 0:
|
||||
print(f"⚠️ Skipped sequence length {sequence_length} due to insufficient data.")
|
||||
continue
|
||||
|
||||
n_features = X.shape[2]
|
||||
|
||||
def build_model(hp):
|
||||
model = Sequential()
|
||||
model.add(Bidirectional(LSTM(units=hp.Int('units', 32, 256, step=2),
|
||||
input_shape=(sequence_length, n_features))))
|
||||
model.add(Dropout(hp.Float('dropout_rate', 0.1, 0.5, step=0.1)))
|
||||
model.add(Dense(len(users), activation='softmax'))
|
||||
model.compile(
|
||||
optimizer=Adam(learning_rate=hp.Choice('learning_rate', [1e-2, 1e-3, 1e-4])),
|
||||
loss='sparse_categorical_crossentropy',
|
||||
metrics=['accuracy']
|
||||
)
|
||||
return model
|
||||
|
||||
tuner = RandomSearch(
|
||||
build_model,
|
||||
objective='val_loss',
|
||||
max_trials=30,
|
||||
executions_per_trial=2,
|
||||
directory='./working/tuner',
|
||||
project_name=f'lstm_seq_{sequence_length}'
|
||||
)
|
||||
|
||||
tuner.search(X, y, epochs=30, validation_data=(X_val, y_val),
|
||||
callbacks=[early_stopping, lr_scheduler], verbose=1)
|
||||
|
||||
best_hps = tuner.get_best_hyperparameters(1)[0]
|
||||
best_model = tuner.hypermodel.build(best_hps)
|
||||
best_model.fit(X, y, epochs=30, validation_data=(X_val, y_val),
|
||||
callbacks=[early_stopping, lr_scheduler], verbose=0)
|
||||
|
||||
best_models[sequence_length] = {
|
||||
'model': best_model,
|
||||
'best_hyperparameters': {
|
||||
'units': best_hps.get('units'),
|
||||
'dropout_rate': best_hps.get('dropout_rate'),
|
||||
'learning_rate': best_hps.get('learning_rate')
|
||||
}
|
||||
}
|
||||
|
||||
# === Run evaluation for each trained sequence length ===
|
||||
test_scenario = get_user_input_for_test()
|
||||
test_data = filter_test_data(df, test_scenario)
|
||||
|
||||
output_excel_path = "./working/evaluation_results.xlsx"
|
||||
|
||||
with ExcelWriter(output_excel_path) as writer:
|
||||
for sequence_length, result in best_models.items():
|
||||
print(f"\n🔍 Testing Model for Sequence Length: {sequence_length}")
|
||||
evaluate_model_on_test_data(
|
||||
result['model'],
|
||||
test_data.copy(),
|
||||
sequence_length,
|
||||
writer # 👈 pass the writer
|
||||
)
|
||||
|
||||
print(f"\n✅ All evaluations completed. Results saved to: {output_excel_path}")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,466 @@
|
||||
import random
|
||||
|
||||
import keras_tuner
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import shutil
|
||||
|
||||
from keras import Input
|
||||
from keras.src.losses import SparseCategoricalCrossentropy
|
||||
from keras.src.metrics import F1Score, Precision, Recall, Accuracy, SparseCategoricalAccuracy
|
||||
from pandas import ExcelWriter, DataFrame
|
||||
from tensorflow.keras.models import Sequential
|
||||
from tensorflow.keras.layers import LSTM, Dense, Dropout, Bidirectional,GRU
|
||||
from tensorflow.keras.optimizers import Adam
|
||||
from tensorflow.keras.callbacks import ReduceLROnPlateau, EarlyStopping
|
||||
from keras_tuner import RandomSearch
|
||||
from sklearn.metrics import accuracy_score, f1_score, precision_score, recall_score, confusion_matrix
|
||||
|
||||
epochs = 5#50
|
||||
model_type_gru = 'GRU'
|
||||
model_type_lstm = 'LSTM'
|
||||
model_type_bilstm = 'BiLSTM'
|
||||
|
||||
|
||||
# === Data functions ===
|
||||
def load_dataset(file_path):
|
||||
return pd.read_excel(file_path)
|
||||
|
||||
def filter_data(df, scenario, ALLUSERS32_15MIN_WITHOUTREHOLD):
|
||||
filtered = pd.DataFrame()
|
||||
for year, months in scenario:
|
||||
filtered = pd.concat([filtered, df[(df['Year'] == year) & (df['Month'].isin(months))]])
|
||||
|
||||
if ALLUSERS32_15MIN_WITHOUTREHOLD:
|
||||
return filtered.drop(columns=['Month', 'Year', 'date', 'DayOfWeek'])
|
||||
else:
|
||||
return filtered.drop(columns=['Month', 'Year', 'date'])
|
||||
|
||||
def filter_test_data(df, scenario):
|
||||
data_parts = []
|
||||
for year, months in scenario:
|
||||
part = df[(df['Year'] == year) & (df['Month'].isin(months))]
|
||||
data_parts.append(part)
|
||||
return pd.concat(data_parts, ignore_index=True)
|
||||
|
||||
def prepare_user_data(df):
|
||||
#df_sorted = df.sort_values(by='user').reset_index(drop=True)
|
||||
users = df['user'].unique()
|
||||
return {user: df[df['user'] == user] for user in users}
|
||||
|
||||
def make_sequences(data, sequence_length):
|
||||
x, y = [], []
|
||||
features = data.drop('user', axis=1).values
|
||||
labels = data['user'].values
|
||||
for i in range(len(features) - sequence_length+1): # with overlap on days
|
||||
# for i in range(0, len(features) - sequence_length + 1, sequence_length): # without overlap on days
|
||||
x.append(features[i:i + sequence_length])
|
||||
y.append(labels[i + sequence_length-1])
|
||||
return x, y
|
||||
|
||||
def prepare_data_for_basic_algorithm(user_data, sequence_length):
|
||||
combined = pd.DataFrame()
|
||||
for user, data in user_data.items():
|
||||
x_new, y_new = make_sequences(data, sequence_length)
|
||||
if len(x_new)>0:
|
||||
var = [[pd.DataFrame(a[s]) for s in range(sequence_length)] for a in x_new]
|
||||
df_var = pd.concat([pd.concat(seq_list).T for seq_list in var])
|
||||
df_var['user'] = user
|
||||
combined = pd.concat([combined, df_var], ignore_index=True)
|
||||
return combined.drop(columns=['user']), combined['user']
|
||||
|
||||
def prepare_data_for_model(user_data, sequence_length, print_counts=False):
|
||||
x, y = [], []
|
||||
combined = pd.DataFrame()
|
||||
for user, data in user_data.items():
|
||||
x_new, y_new = make_sequences(data, sequence_length)
|
||||
x = x + x_new
|
||||
y = y + y_new
|
||||
if print_counts and len(x_new)>0:
|
||||
var = [[pd.DataFrame(a[s])for s in range(sequence_length)] for a in x_new ]
|
||||
df_var = pd.concat([pd.concat(seq_list).T for seq_list in var])
|
||||
df_var['user'] = user
|
||||
combined = pd.concat([combined, df_var], ignore_index=True)
|
||||
if print_counts:
|
||||
combined_ohne = combined.drop('user', axis=1)
|
||||
print('Alle', len(combined))
|
||||
print('Unique mit user', len(combined.drop_duplicates()))
|
||||
print('Unique ohne user', len(combined_ohne.drop_duplicates()))
|
||||
print('Unique')
|
||||
print(combined.drop_duplicates()['user'].value_counts())
|
||||
print('Alle')
|
||||
print(combined['user'].value_counts())
|
||||
random.Random(17).shuffle(x)
|
||||
random.Random(17).shuffle(y)
|
||||
x = np.array(x)
|
||||
y = np.array(y)
|
||||
return x,y
|
||||
|
||||
# === Training & Validation ===
|
||||
def train_models(user_data, user_data_val, sequence_lengths, tuner_dir="./working/tuner", model_type=model_type_lstm):
|
||||
best_models = {}
|
||||
early_stopping = EarlyStopping(monitor='val_loss', patience=5, restore_best_weights=True)
|
||||
lr_scheduler = ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=5, verbose=1)
|
||||
users = list(user_data.keys())
|
||||
|
||||
shutil.rmtree(tuner_dir, ignore_errors=True)
|
||||
|
||||
for sequence_length in sequence_lengths:
|
||||
print(f"\n=== Training for Sequence Length: {sequence_length} ===")
|
||||
X, y = prepare_data_for_model(user_data=user_data, sequence_length=sequence_length)
|
||||
X_val, y_val = prepare_data_for_model(user_data=user_data_val, sequence_length=sequence_length)
|
||||
|
||||
if X.shape[0] == 0 or X_val.shape[0] == 0:
|
||||
print(f"⚠️ Skipped sequence length {sequence_length} due to insufficient data.")
|
||||
continue
|
||||
|
||||
n_features = X.shape[2]
|
||||
|
||||
def build_model(hp):
|
||||
model = Sequential()
|
||||
if model_type==model_type_bilstm:
|
||||
model.add(Bidirectional(LSTM(units=hp.Int('units', 32, 256, step=2),
|
||||
input_shape=(sequence_length, n_features))))
|
||||
if model_type==model_type_lstm:
|
||||
model.add(LSTM(units=hp.Int('units', 32, 256, step=2),
|
||||
input_shape=(sequence_length, n_features)))
|
||||
if model_type==model_type_gru:
|
||||
model.add(GRU(units=hp.Int('units', 32, 256, step=2),
|
||||
input_shape=(sequence_length, n_features)))
|
||||
model.add(Dropout(hp.Float('dropout_rate', 0.1, 0.5, step=0.1)))
|
||||
model.add(Dense(len(users), activation='softmax'))
|
||||
model.compile(
|
||||
optimizer=Adam(learning_rate=hp.Choice('learning_rate', [1e-2, 1e-3, 1e-4])),
|
||||
loss='sparse_categorical_crossentropy',
|
||||
metrics=['accuracy']
|
||||
)
|
||||
return model
|
||||
|
||||
tuner = RandomSearch(
|
||||
build_model,
|
||||
objective='val_loss',
|
||||
max_trials=30,
|
||||
executions_per_trial=2,
|
||||
directory=tuner_dir,
|
||||
project_name=f'lstm_seq_{sequence_length}'
|
||||
)
|
||||
|
||||
tuner.search(X, y, epochs=epochs, validation_data=(X_val, y_val),
|
||||
callbacks=[early_stopping, lr_scheduler], verbose=0)
|
||||
|
||||
best_hps = tuner.get_best_hyperparameters(1)[0]
|
||||
best_model = tuner.hypermodel.build(best_hps)
|
||||
best_model.fit(X, y, epochs=epochs, validation_data=(X_val, y_val),
|
||||
callbacks=[early_stopping, lr_scheduler], verbose=0)
|
||||
|
||||
best_models[sequence_length] = {
|
||||
'model': best_model,
|
||||
'best_hyperparameters': {
|
||||
'units': best_hps.get('units'),
|
||||
'dropout_rate': best_hps.get('dropout_rate'),
|
||||
'learning_rate': best_hps.get('learning_rate')
|
||||
}
|
||||
}
|
||||
|
||||
return best_models
|
||||
|
||||
# === Training & Validation ===
|
||||
def train_models_v2(user_data, user_data_val, sequence_length, model_type):
|
||||
tuner_dir = "./working/tuner/"+model_type
|
||||
#val_metric = 'val_f1'
|
||||
val_metric = 'val_precision'
|
||||
|
||||
early_stopping = EarlyStopping(monitor=val_metric, patience=3, restore_best_weights=True)
|
||||
lr_scheduler = ReduceLROnPlateau(monitor=val_metric, factor=0.5, patience=2)
|
||||
|
||||
shutil.rmtree(tuner_dir, ignore_errors=True)
|
||||
|
||||
x, y = prepare_data_for_model(user_data=user_data, sequence_length=sequence_length)
|
||||
x_val, y_val = prepare_data_for_model(user_data=user_data_val, sequence_length=sequence_length)
|
||||
|
||||
n_features = x.shape[2]
|
||||
users = list(user_data.keys())
|
||||
|
||||
#y_val = np.array(y_val).reshape(-1, 1)
|
||||
#y = np.array(y).reshape(-1, 1)
|
||||
|
||||
def build_model(hp):
|
||||
units_hp = hp.Int('units', 2, 8, step=2, sampling="log")
|
||||
# units_hp = hp.Int('units', 2, 256, step=2, sampling="log")
|
||||
|
||||
model = Sequential()
|
||||
model.add(Input((sequence_length, n_features)))
|
||||
if model_type==model_type_bilstm:
|
||||
model.add(Bidirectional(LSTM(units=units_hp)))
|
||||
if model_type==model_type_lstm:
|
||||
model.add(LSTM(units=units_hp))
|
||||
if model_type==model_type_gru:
|
||||
model.add(GRU(units=units_hp))
|
||||
model.add(Dropout(hp.Float('dropout_rate', 0.1, 0.2, step=0.1)))
|
||||
model.add(Dense(len(users), activation='softmax'))
|
||||
model.compile(
|
||||
optimizer=Adam(learning_rate=hp.Choice('learning_rate', [1e-5])),
|
||||
loss='sparse_categorical_crossentropy',
|
||||
metrics=[#F1Score(name='f1', average='weighted'),
|
||||
Precision(), #Recall(), Accuracy()
|
||||
]
|
||||
)
|
||||
return model
|
||||
|
||||
tuner = RandomSearch(
|
||||
build_model,
|
||||
objective=keras_tuner.Objective(val_metric, direction="max"),
|
||||
max_trials=120,
|
||||
directory=tuner_dir,
|
||||
)
|
||||
|
||||
tuner.search(x, y, epochs=epochs, validation_data=(x_val, y_val),
|
||||
callbacks=[early_stopping, lr_scheduler])
|
||||
return tuner.get_best_models(num_models=1)[0]
|
||||
|
||||
|
||||
def train_one_model(train_data, val_data, n_batch, n_epochs, n_neurons,n_neurons2,n_neurons3,n_neurons4, l_rate, d1, r1, reg1, r2, reg2, sequence_length, model_type):
|
||||
x, y = prepare_data_for_model(user_data=train_data, sequence_length=sequence_length)
|
||||
n_features = x.shape[2]
|
||||
users = list(train_data.keys())
|
||||
|
||||
# prepare model
|
||||
def build_model():
|
||||
model = Sequential()
|
||||
model.add(Input(shape=(sequence_length, n_features), batch_size=n_batch))
|
||||
if model_type == model_type_bilstm:
|
||||
model.add(Bidirectional(LSTM(n_neurons)))
|
||||
if model_type == model_type_lstm:
|
||||
# model.add(LSTM(n_neurons, kernel_regularizer=reg1, return_sequences=True))
|
||||
model.add(LSTM(n_neurons))
|
||||
# model.add(LSTM(n_neurons2))
|
||||
if model_type == model_type_gru:
|
||||
model.add(GRU(n_neurons))
|
||||
#model.add(Dense(n_neurons, activation='relu'))
|
||||
#model.add(Dropout(d1))
|
||||
model.add(Dense(len(users), activation='softmax'))
|
||||
model.compile(
|
||||
optimizer=Adam(learning_rate=l_rate),
|
||||
loss=SparseCategoricalCrossentropy(),
|
||||
metrics=[SparseCategoricalAccuracy()],
|
||||
)
|
||||
return model
|
||||
|
||||
model = build_model()
|
||||
|
||||
# fit model
|
||||
train_acc, test_acc, train_p, test_p, train_r, test_r, train_f1, test_f1 = list(), list(),list(), list(),list(), list(),list(), list()
|
||||
for i in range(n_epochs):
|
||||
model.fit(x, y, batch_size=n_batch, epochs=1, verbose=0, shuffle=False)
|
||||
# evaluate model on train data
|
||||
acc, p, r, f1 = evaluate(model, train_data, sequence_length, n_batch)
|
||||
train_acc.append(acc)
|
||||
train_p.append(p)
|
||||
train_r.append(r)
|
||||
train_f1.append(f1)
|
||||
# evaluate model on test data
|
||||
savename = 'cf_matrix_'+get_save_id(n_epochs, n_neurons, n_neurons2,n_neurons3, n_neurons4, n_batch, l_rate,d1,r1, r2)+'.json'
|
||||
acc, p, r, f1 = evaluate(model, val_data, sequence_length, n_batch, save_name=savename)
|
||||
test_acc.append(acc)
|
||||
test_p.append(p)
|
||||
test_r.append(r)
|
||||
test_f1.append(f1)
|
||||
|
||||
history = DataFrame()
|
||||
history['train_acc'], history['test_acc'] = train_acc, test_acc
|
||||
history['train_p'], history['test_p'] = train_p, test_p
|
||||
history['train_r'], history['test_r'] = train_r, test_r
|
||||
history['train_f1'], history['test_f1'] = train_f1, test_f1
|
||||
return history
|
||||
|
||||
|
||||
def train_one_model_v2(train_data, val_data, n_batch, n_epochs, n_neurons, sequence_length, model_type):
|
||||
x, y = train_data
|
||||
x_v, y_v = val_data
|
||||
users = list(set(y))
|
||||
|
||||
# renumber users
|
||||
user_map = {users[i]:i for i in range(len(users))}
|
||||
y = np.array([user_map[x] for x in y])
|
||||
y_v = np.array([user_map[x] for x in y_v])
|
||||
n_features = x.shape[2]
|
||||
user_num = len(users)
|
||||
|
||||
# prepare model
|
||||
def build_model():
|
||||
model = Sequential()
|
||||
model.add(Input(shape=(sequence_length, n_features), batch_size=n_batch))
|
||||
if model_type == model_type_bilstm:
|
||||
model.add(Bidirectional(LSTM(n_neurons)))
|
||||
if model_type == model_type_lstm:
|
||||
model.add(LSTM(n_neurons))
|
||||
if model_type == model_type_gru:
|
||||
model.add(GRU(n_neurons))
|
||||
#model.add(Dense(n_neurons, activation='relu'))
|
||||
#model.add(Dropout(d1))
|
||||
model.add(Dense(user_num, activation='softmax'))
|
||||
model.compile(
|
||||
optimizer=Adam(),
|
||||
loss=SparseCategoricalCrossentropy(),
|
||||
metrics=[SparseCategoricalAccuracy()],
|
||||
)
|
||||
return model
|
||||
|
||||
model = build_model()
|
||||
|
||||
# fit model
|
||||
train_acc, test_acc, train_p, test_p, train_r, test_r, train_f1, test_f1 = list(), list(),list(), list(),list(), list(),list(), list()
|
||||
for i in range(n_epochs):
|
||||
model.fit(x, y, batch_size=n_batch, epochs=1, verbose=0, shuffle=False)
|
||||
# evaluate model on train data
|
||||
acc, p, r, f1 = evaluate_v2(model, (x,y), sequence_length, n_batch)
|
||||
train_acc.append(acc)
|
||||
train_p.append(p)
|
||||
train_r.append(r)
|
||||
train_f1.append(f1)
|
||||
# evaluate model on test data
|
||||
savename = 'cf_matrix_'+get_save_id(n_epochs, n_neurons, '', '', '', n_batch, '','','','')+'.json'
|
||||
acc, p, r, f1 = evaluate_v2(model, (x_v, y_v), sequence_length, n_batch, save_name=savename)
|
||||
test_acc.append(acc)
|
||||
test_p.append(p)
|
||||
test_r.append(r)
|
||||
test_f1.append(f1)
|
||||
|
||||
history = DataFrame()
|
||||
history['train_acc'], history['test_acc'] = train_acc, test_acc
|
||||
history['train_p'], history['test_p'] = train_p, test_p
|
||||
history['train_r'], history['test_r'] = train_r, test_r
|
||||
history['train_f1'], history['test_f1'] = train_f1, test_f1
|
||||
return history
|
||||
|
||||
def get_save_id(n_epochs, n_neurons, n_neurons2,n_neurons3,n_neurons4, n_batch, l_rate, d1,r1, r2):
|
||||
return '_e'+str(n_epochs)+'_n'+str(n_neurons)+'_b'+ str(n_batch)
|
||||
#'x'+str(n_neurons3)+'x'+str(n_neurons4)
|
||||
#+'_l'+str(l_rate)+'_r'+str(r1)+'xx'+str(r2)
|
||||
|
||||
|
||||
def evaluate(model, df, sequence_length, batch_size, save_name=None):
|
||||
x, y = prepare_data_for_model(user_data=df, sequence_length=sequence_length)
|
||||
x = np.array(x)
|
||||
y_true = np.array(y)
|
||||
|
||||
y_pred = model.predict(x, verbose=0, batch_size=batch_size)
|
||||
y_pred_classes = np.argmax(y_pred, axis=1)
|
||||
cf_matrix = pd.DataFrame(confusion_matrix(y_true, y_pred_classes))
|
||||
if save_name is not None:
|
||||
cf_matrix.to_json('results/'+save_name)
|
||||
true_counts = pd.DataFrame(y).value_counts()
|
||||
print('Top true occurrences', true_counts[:6])
|
||||
predicted_counts = pd.DataFrame(y_pred_classes).value_counts()
|
||||
print('Top predicted occurrences', predicted_counts[:6])
|
||||
|
||||
return eval_metrics(y_true=y_true, y_pred=y_pred_classes)
|
||||
|
||||
def evaluate_v2(model, data, sequence_length, batch_size, save_name=None):
|
||||
x, y_true = data
|
||||
y_pred = model.predict(x, verbose=0, batch_size=batch_size)
|
||||
y_pred_classes = np.argmax(y_pred, axis=1)
|
||||
cf_matrix = pd.DataFrame(confusion_matrix(y_true, y_pred_classes))
|
||||
if save_name is not None:
|
||||
cf_matrix.to_json('results/'+save_name)
|
||||
true_counts = pd.DataFrame(y_true).value_counts()
|
||||
print('Top true occurrences', true_counts[:6])
|
||||
predicted_counts = pd.DataFrame(y_pred_classes).value_counts()
|
||||
print('Top predicted occurrences', predicted_counts[:6])
|
||||
|
||||
return eval_metrics(y_true=y_true, y_pred=y_pred_classes)
|
||||
|
||||
|
||||
def eval_metrics(y_true, y_pred):
|
||||
f1 = f1_score(y_true=y_true, y_pred=y_pred, average='weighted')
|
||||
p = precision_score(y_true=y_true, y_pred=y_pred, average='weighted')
|
||||
r = recall_score(y_true=y_true, y_pred=y_pred, average='weighted')
|
||||
acc = accuracy_score(y_true=y_true, y_pred=y_pred)
|
||||
return acc, p, r, f1
|
||||
|
||||
# === Evaluation ===
|
||||
def evaluate_models(best_models, df_test, sequence_lengths, output_excel_path, ALLUSERS32_15MIN_WITHOUTTHREHOLD):
|
||||
print("\n🧪 Evaluating on Test Data...")
|
||||
with ExcelWriter(output_excel_path) as writer:
|
||||
for sequence_length in sequence_lengths:
|
||||
if sequence_length not in best_models:
|
||||
continue
|
||||
evaluate_model_on_test_data(best_models[sequence_length]['model'], df_test.copy(),
|
||||
sequence_length, writer, ALLUSERS32_15MIN_WITHOUTTHREHOLD)
|
||||
|
||||
def evaluate_model_on_test_data(model, test_df, sequence_length, excel_writer, ALLUSERS32_15MIN_WITHOUTTHREHOLD):
|
||||
if(ALLUSERS32_15MIN_WITHOUTTHREHOLD):
|
||||
test_df = test_df.drop(columns=['Month', 'Year', 'date', 'DayOfWeek'])
|
||||
else:
|
||||
test_df = test_df.drop(columns=['Month', 'Year', 'date'])
|
||||
|
||||
test_df = test_df.sort_values(by='user').reset_index(drop=True)
|
||||
|
||||
users = test_df['user'].unique()
|
||||
results = []
|
||||
accuracy_above_50 = 0
|
||||
|
||||
for user in users:
|
||||
user_df = test_df[test_df['user'] == user]
|
||||
X, y_true = [], []
|
||||
user_features = user_df.drop(columns=['user']).values
|
||||
user_labels = user_df['user'].values
|
||||
|
||||
if len(user_df) <= sequence_length:
|
||||
print(f"Skipping User {user} (not enough data for sequence length {sequence_length})")
|
||||
continue
|
||||
|
||||
for i in range(len(user_df) - sequence_length):
|
||||
seq_x = user_features[i:i + sequence_length]
|
||||
seq_y = user_labels[i + sequence_length]
|
||||
X.append(seq_x)
|
||||
y_true.append(seq_y)
|
||||
|
||||
X = np.array(X)
|
||||
y_true = np.array(y_true)
|
||||
|
||||
if len(X) == 0:
|
||||
continue
|
||||
|
||||
y_pred = model.predict(X, verbose=0)
|
||||
y_pred_classes = np.argmax(y_pred, axis=1)
|
||||
|
||||
# counts which class was predicted how often
|
||||
unique_pred, counts_pred = np.unique(y_pred_classes, return_counts=True)
|
||||
label_counts_pred = dict(zip(unique_pred, counts_pred))
|
||||
|
||||
# counts which class should have been predicted how often (only one class for the user)
|
||||
unique_true, counts_true = np.unique(y_true, return_counts=True)
|
||||
label_counts_true = dict(zip(unique_true, counts_true))
|
||||
|
||||
# the fraction of correctly classified samples
|
||||
acc = accuracy_score(y_true, y_pred_classes)
|
||||
if acc > 0.5:
|
||||
accuracy_above_50 += 1
|
||||
|
||||
results.append({
|
||||
'User': user,
|
||||
'Accuracy (%)': acc * 100,
|
||||
'Predicted Class Distribution': str(label_counts_pred),
|
||||
'Actual Class Distribution': str(label_counts_true)
|
||||
})
|
||||
|
||||
print(f"\n=== User {user} ===")
|
||||
print(f"✅ Accuracy: {acc * 100:.2f}%")
|
||||
print("📊 Predicted Class Distribution:", label_counts_pred)
|
||||
print("📌 Actual Class Distribution: ", label_counts_true)
|
||||
|
||||
final_accuracy_percent = (accuracy_above_50 / 32) * 100
|
||||
print(f"\n🟩 Final Evaluation Summary for Sequence Length {sequence_length}:")
|
||||
print(f"Users with >50% Accuracy: {accuracy_above_50} / 32")
|
||||
print(f"✅ Final Success Rate: {final_accuracy_percent:.2f}%")
|
||||
|
||||
results.append({
|
||||
'User': 'TOTAL',
|
||||
'Accuracy (%)': '',
|
||||
'Predicted Class Distribution': f'Users >50% Acc: {accuracy_above_50}/32',
|
||||
'Actual Class Distribution': f'Success Rate: {final_accuracy_percent:.2f}%'
|
||||
})
|
||||
|
||||
df_results = pd.DataFrame(results)
|
||||
df_results.to_excel(excel_writer, sheet_name=f"SeqLen_{sequence_length}", index=False)
|
||||
@@ -0,0 +1,49 @@
|
||||
absl-py==2.3.1
|
||||
astunparse==1.6.3
|
||||
certifi==2025.7.14
|
||||
charset-normalizer==3.4.2
|
||||
et_xmlfile==2.0.0
|
||||
flatbuffers==25.2.10
|
||||
gast==0.6.0
|
||||
google-pasta==0.2.0
|
||||
grpcio==1.73.1
|
||||
h5py==3.14.0
|
||||
idna==3.10
|
||||
joblib==1.5.1
|
||||
keras==3.10.0
|
||||
keras-tuner==1.4.7
|
||||
kt-legacy==1.0.5
|
||||
libclang==18.1.1
|
||||
Markdown==3.8.2
|
||||
markdown-it-py==3.0.0
|
||||
MarkupSafe==3.0.2
|
||||
mdurl==0.1.2
|
||||
ml_dtypes==0.5.1
|
||||
namex==0.1.0
|
||||
numpy==2.1.3
|
||||
openpyxl==3.1.5
|
||||
opt_einsum==3.4.0
|
||||
optree==0.16.0
|
||||
packaging==25.0
|
||||
pandas==2.3.1
|
||||
protobuf==5.29.5
|
||||
Pygments==2.19.2
|
||||
python-dateutil==2.9.0.post0
|
||||
pytz==2025.2
|
||||
requests==2.32.4
|
||||
rich==14.0.0
|
||||
scikit-learn==1.7.1
|
||||
scipy==1.16.0
|
||||
six==1.17.0
|
||||
tensorboard==2.19.0
|
||||
tensorboard-data-server==0.7.2
|
||||
tensorflow==2.20.0
|
||||
termcolor==3.1.0
|
||||
threadpoolctl==3.6.0
|
||||
typing_extensions==4.14.1
|
||||
tzdata==2025.2
|
||||
urllib3==2.5.0
|
||||
Werkzeug==3.1.3
|
||||
wrapt==1.17.2
|
||||
|
||||
matplotlib~=3.10.6
|
||||
Reference in New Issue
Block a user