Restructuring

This commit is contained in:
bs
2026-07-02 14:17:22 +02:00
parent 1d14bc0c8d
commit 0f123471f2
17 changed files with 1719 additions and 901 deletions
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
+180
View File
@@ -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")
+36
View File
@@ -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)
+987
View File
@@ -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
View File
@@ -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])] # JanFeb 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')
+296
View File
@@ -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}")
+466
View File
@@ -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)
+49
View File
@@ -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