Skip to content

Commit 5727948

Browse files
sync
1 parent dd39885 commit 5727948

2 files changed

Lines changed: 140 additions & 7 deletions

File tree

sqlite_db/sqlite_utils.py

Lines changed: 113 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,48 @@
55

66

77
class SQLDb:
8+
"""
9+
SQLite database handler for managing TSNE data visualization points.
10+
Provides methods for creating, storing, and retrieving 3D visualization data
11+
with associated metadata like original labels and classified labels.
12+
"""
13+
814
def __init__(self, table_name="", db_path="manifolk.db", create_table=True):
15+
# The commented code below was likely a path validation check
916
# if not Path(db_path).is_file():
1017
# raise Exception(f"The Database path should be a full path of a file even if it doesnt exist. "
1118
# f"A new databse will be created at that path. Please pass full file path name")
19+
20+
# Store the database file path
1221
self.db_path = db_path
22+
23+
# Create a unique table name by appending current timestamp to avoid collisions
1324
datetime_now = datetime.now().strftime("%Y-%m-%d-%H:%M:%S")
1425
self.table_name = f"[{table_name}_{datetime_now}]"
26+
27+
# Initialize connection objects to None
1528
self.conn = None
1629
self.table_cursor = None
30+
31+
# Optionally create the table immediately (default behavior)
1732
if create_table:
1833
self.create_table()
1934

2035
def create_table(self):
36+
"""
37+
Create a new SQLite table with the schema needed for storing TSNE visualization data.
38+
Schema includes:
39+
- epoch: training epoch/iteration number
40+
- X, Y, Z: 3D coordinates for visualization
41+
- DATAPOINT_NAME: unique identifier for each data point
42+
- ORIGINAL_LABEL: ground truth label
43+
- CLASSIFIED_AS_LABEL: model's predicted label
44+
"""
45+
# Establish connection to the SQLite database
2146
self.conn = sqlite3.connect(self.db_path)
2247
self.table_cursor = self.conn.cursor()
48+
49+
# Execute SQL to create the table with required columns
2350
self.table_cursor.execute(
2451
f"CREATE TABLE "
2552
f"{self.table_name} "
@@ -31,40 +58,79 @@ def create_table(self):
3158
f"ORIGINAL_LABEL text, "
3259
f"CLASSIFIED_AS_LABEL text)"
3360
)
61+
# Commit the changes to the database
3462
self.conn.commit()
3563

3664
def sanitize_input(self, *args):
65+
"""
66+
Validate input types before inserting into the database.
67+
Ensures data integrity by checking that each value has the expected type.
68+
69+
Args:
70+
*args: A sequence of values to validate in the following order:
71+
epoch (int), x (float), y (float), z (float),
72+
datapoint_name (str), original_label (str), classified_label (str)
73+
74+
Raises:
75+
Exception: If any input value doesn't match its expected type
76+
"""
77+
# Validate epoch is an integer
3778
epoch = args[0]
3879
if not isinstance(epoch, int):
39-
raise Exception(f"The input x is not a int value: {epoch}")
80+
raise Exception(f"The input epoch is not an int value: {epoch}")
81+
82+
# Validate X coordinate is a float
4083
x = args[1]
4184
if not (isinstance(x, float) or isinstance(x, np.float32)):
4285
raise Exception(f"The input x is not a float value: {x}")
86+
87+
# Validate Y coordinate is a float
4388
y = args[2]
4489
if not (isinstance(y, float) or isinstance(y, np.float32)):
45-
raise Exception(f"The input x is not a float value: {y}")
90+
raise Exception(f"The input y is not a float value: {y}")
91+
92+
# Validate Z coordinate is a float
4693
z = args[3]
4794
if not (isinstance(z, float) or isinstance(z, np.float32)):
48-
raise Exception(f"The input x is not a float value: {z}")
95+
raise Exception(f"The input z is not a float value: {z}")
96+
97+
# Validate datapoint_name is a string
4998
datapoint_name = args[4]
5099
if not isinstance(datapoint_name, str):
51-
raise Exception(f"The input x is not a string value: {datapoint_name}")
100+
raise Exception(f"The input datapoint_name is not a string value: {datapoint_name}")
101+
102+
# Validate original_label is a string
52103
original_label = args[5]
53104
if not isinstance(original_label, str):
54-
raise Exception(f"The input x is not a string value: {original_label}")
105+
raise Exception(f"The input original_label is not a string value: {original_label}")
106+
107+
# Validate classified_label is a string
55108
classified_label = args[6]
56109
if not isinstance(classified_label, str):
57-
raise Exception(f"The input x is not a string value: {classified_label}")
110+
raise Exception(f"The input classified_label is not a string value: {classified_label}")
58111

59112
def insert_entry(self, *args):
113+
"""
114+
Insert a single data point entry into the database.
115+
116+
Args:
117+
*args: A sequence of values in the following order:
118+
epoch (int), x (float), y (float), z (float),
119+
datapoint_name (str), original_label (str), classified_label (str)
120+
"""
121+
# Validate input types
60122
self.sanitize_input(*args)
123+
124+
# Extract values from args
61125
epoch = args[0]
62126
x = args[1]
63127
y = args[2]
64128
z = args[3]
65129
datapoint_name = args[4]
66130
original_label = args[5]
67131
classified_label = args[6]
132+
133+
# Execute SQL INSERT statement
68134
self.table_cursor.execute(
69135
f"""INSERT INTO {self.table_name} VALUES
70136
("{epoch}",
@@ -75,31 +141,72 @@ def insert_entry(self, *args):
75141
"{original_label}",
76142
"{classified_label}")"""
77143
)
144+
# Commit the transaction
78145
self.conn.commit()
79146

80147
def insert(
81148
self, epoch: int, tsne_array: np.array, original_labels: list, predicted_labels: list, datapoint_ids: list
82149
):
150+
"""
151+
Insert multiple data points for a given epoch into the database.
152+
153+
Args:
154+
epoch (int): The training epoch/iteration number
155+
tsne_array (np.array): Array of 3D points from TSNE dimensionality reduction
156+
original_labels (list): List of ground truth labels for each point
157+
predicted_labels (list): List of model-predicted labels for each point
158+
datapoint_ids (list): List of unique identifiers for each data point
159+
"""
160+
# Iterate through all data points and their associated metadata
83161
for each_point, original_label, predicted_label, datapoint_id in zip(
84162
tsne_array, original_labels, predicted_labels, datapoint_ids
85163
):
164+
# Unpack X, Y, Z coordinates from the point
86165
x, y, z = each_point
166+
# Insert each point as an individual entry
87167
self.insert_entry(epoch, x, y, z, datapoint_id, original_label, predicted_label)
88168

89169
def get_all_table_names(self):
170+
"""
171+
Retrieve all table names from the SQLite database.
172+
173+
Returns:
174+
list: List of all table names in the database with square brackets
175+
to handle special characters in table names
176+
"""
177+
# Create a new connection to the database
90178
conn = sqlite3.connect(self.db_path)
179+
# Query the sqlite_master table to get all table names
91180
res = conn.execute("SELECT name FROM sqlite_master WHERE type='table';")
92181
all_table_names = []
182+
# Process results and format table names with square brackets
93183
for name in res:
94184
all_table_names.append(f"[{name[0]}]")
185+
# Close the connection
95186
conn.close()
96187
return all_table_names
97188

98189
def get_pandas_frame(self, table_name):
190+
"""
191+
Load a table from the database into a pandas DataFrame.
192+
193+
Args:
194+
table_name (str): Name of the table to load
195+
196+
Returns:
197+
pandas.DataFrame: DataFrame containing all data from the specified table
198+
"""
199+
# Create a new connection to the database
99200
conn = sqlite3.connect(self.db_path)
201+
# Use pandas to read the SQL query result directly into a DataFrame
100202
df = pd.read_sql_query(f"SELECT * from {table_name}", conn)
203+
# Close the connection
101204
conn.close()
102205
return df
103206

104207
def close_connection(self):
208+
"""
209+
Close the database connection.
210+
Should be called when the database operations are complete.
211+
"""
105212
self.conn.close()

tsne_plots/tsne_plot.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,26 +170,52 @@ def update_figure(*vals):
170170
df = sql_db.get_pandas_frame(vals[4])
171171
filtered_df = df[df["ORIGINAL_LABEL"].isin(selected_labels)]
172172
filtered_df = filtered_df.loc[filtered_df["epoch"] == slider_value]
173+
174+
# Calculate accuracy using original labels vs. classified labels for the filtered dataset
173175
accuracy = get_accuracy(filtered_df)
176+
177+
# Generate the plotly figure using the filtered dataframe with current selection parameters
174178
fig = get_plotly_fig(filtered_df, df)
179+
180+
# Preserve the current camera view position when the figure is updated
181+
# This prevents the view from resetting when filtering changes
175182
fig.update_layout(scene_camera=data["scene.camera"])
183+
184+
# Handle the case where a specific datapoint UID is provided for highlighting
176185
if datapoint_uid:
186+
# Attempt to find the x, y, z coordinates for the specified UID in the current filtered data
177187
point_x, point_y, point_z = get_uid_xyz(filtered_df, datapoint_uid)
188+
189+
# If the point exists in the current filtered dataset
178190
if point_x and point_y and point_z:
191+
# Add a special diamond-shaped marker to highlight the selected datapoint
179192
fig.add_trace(
180193
Scatter3d(x=[point_x], y=[point_y], z=[point_z], mode="markers", marker=dict(symbol="diamond"))
181194
)
195+
# Set flag to indicate the point was found (used for form validation feedback)
182196
set_radio_button = "YES"
183197
else:
184-
# The point doesn't exist, show it to user
198+
# The point doesn't exist in the current filtered data
199+
# This could be because:
200+
# 1. The UID doesn't exist in the database
201+
# 2. The UID exists but not in the current filtered view (wrong epoch or label)
185202
set_radio_button = "NO"
186203
else:
204+
# No UID was provided, so no specific point to highlight
187205
set_radio_button = "NO"
188206

207+
# Return multiple outputs for the callback:
208+
# 1. The updated figure with new filters and possibly highlighted point
209+
# 2. The accuracy percentage formatted with 2 decimal places
210+
# 3. Whether the input UID is valid (for positive form feedback)
211+
# 4. Whether the input UID is invalid (for negative form feedback)
189212
return fig, f"{accuracy:.2f}%", set_radio_button == "YES", set_radio_button != "YES"
190213

214+
# Start the Dash server
215+
# debug=False prevents auto-reloading when code changes
191216
app.run_server(debug=False)
192217

193218

194219
if __name__ == "__main__":
220+
# Entry point of the script when executed directly
195221
main()

0 commit comments

Comments
 (0)