Rule-based classification of change events¶
This Jupyter Notebook demonstrates a rule-based classification workflow for analyzing change events. The notebook processes unlabelled change events, applies predefined classification rules, and generates labeled outputs.
Workflow¶
Setup and Configuration:
- Import necessary modules.
- Set the working directory to ensure relative paths resolve correctly.
Input/Output Paths:
- Define the path to the unlabelled change events file (
change_events_file
). - Specify the output folder (
outfolder
) and the path for the labeled output file (labelled_file
).
- Define the path to the unlabelled change events file (
Classification Rules:
- Define a dictionary of rules (
classification_rules
) to classify events based on features such aschange_mean
andhull_volume
.
- Define a dictionary of rules (
Processing:
- Load the unlabelled change events into a
ChangeEventCollection
object (coll
). - Apply the rule-based classifier to generate a labeled DataFrame (
features_df
). - Save the labeled events to the specified output file.
- Load the unlabelled change events into a
Visualization:
- Plot the classified events using scatter plots, with different colors representing different event types.
Outputs¶
- Labeled Events File: A JSON file (
labelled_file
) containing the classified change events. - Visualization: A scatter plot showing the distribution of classified events based on their features.
Imports & working directory¶
In order to ensure all imports are available and that paths are relative to this script’s location, we first import modules and set the working directory.
import os
import matplotlib.pyplot as plt
from aimon import ProjectChange
from aimon import ChangeEventCollection
from aimon import utilities
# Figure out the directory where this notebook/script lives
try:
current_dir = os.path.dirname(os.path.abspath(__file__))
except NameError:
current_dir = os.getcwd()
# Change into that directory so all relative paths resolve correctly
os.chdir(current_dir)
print(current_dir)
Define input/output paths¶
Next we declare where to read our unlabelled events from, and where to write the labelled output.
# Path to the unlabelled change events test file
change_events_file = "../test_data/change_events_unlabelled.json"
# Folder where the labelled output will go
outfolder = "../test_data/out"
# Full path for the labelled file
labelled_file = os.path.join(outfolder, 'change_events_labelled_rule_based.json')
Classification rules definition¶
Each rule is a mapping from feature names (e.g. change_mean) to a dict with optional "min" and/or "max" bounds. If an event satisfies all bounds for a rule, it is assigned that class.
"class_name": {
"feature1": {"min": <lower_bound>, "max": <upper_bound>},
"feature2": {"min": <lower_bound>}
}
classification_rules = {
"large_change": {
"change_mean": {"min": 0.2, "max": 10},
"hull_volume": {"min": 100}
},
"small_change": {
"change_mean": {"max": 0.2}
},
"specific_change": {
"change_mean": {"min": 1, "max": 5},
"hull_volume": {"min": 5, "max": 10}
},
}
Load, classify & save¶
With paths and rules in place, we load the unlabelled events, apply the rule‑based classifier, and save the results.
# Load events from file
coll = ChangeEventCollection().load_from_file(change_events_file)
# Apply our rule‑based classifier to produce a DataFrame of features + event_type
features_df = coll.classify_events_rule_based(classification_rules)
# Write the labelled events back out
coll.save_to_file(labelled_file)
Visualize events clustering¶
Finally, we plot each class with a different color and use a log scale on the y‑axis to handle wide ranges of hull volumes.
# Helper to plot a subset
def plot_events(df, event_type, color, alpha=0.7, size=5):
subset = df[df['event_type'] == event_type]
plt.scatter(subset['change_mean'], subset['hull_volume'],
label=event_type.replace('_', ' ').title(),
alpha=alpha, s=size, color=color)
plt.figure(figsize=(5, 4))
plot_events(features_df, 'large_change', 'red')
plot_events(features_df, 'small_change', 'blue')
plot_events(features_df, 'specific_change','green')
plot_events(features_df, 'unclassified', 'gray', alpha=0.3)
plt.yscale('log')
plt.legend()
plt.xlabel('Change Mean')
plt.ylabel('Hull Volume')
plt.title('Rule‑based Classification of Change Events')
plt.show()
Project changes into GIS format¶
Finally, we wrap up by projecting our labelled events into GeoJSON using ProjectChange.
img_path = "../test_data/RangeImage.tif"
change_prj = ProjectChange(
change_event_file = labelled_file,
project_name = os.path.basename(labelled_file)[:-5],
projected_image_path = img_path,
projected_events_folder = outfolder,
epsg = 31254
)
change_prj.project_change()
# Paths to the vector and raster files
img_path = "../test_data/RangeImage.tif"
change_event_file = "../test_data/out/change_events_labelled_rule_based_change_events_pixel.geojson"
utilities.plot_change_events(change_event_file, img_path, 'event_type', ['#8080FF', 'green', '#8B4513', '#B5179E'])