Skip to content

Commit 22c4b13

Browse files
committedDec 23, 2024·
def has_plateaued
1 parent 2951a72 commit 22c4b13

File tree

3 files changed

+94
-17
lines changed

3 files changed

+94
-17
lines changed
 

‎MetricDB/src/main.py

+91-15
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import sqlite3, pandas
44
from rich import print
55
from pathlib import Path
6+
from scipy import stats
67

78

89
class MetricDB:
@@ -53,9 +54,7 @@ def get_moving_average(self, key: str, name_table: str = "main", window_size: in
5354
return moving_average
5455

5556
if self.verbose:
56-
print(
57-
f"[bold yellow]Warning: No valid numeric values found for key '{key}' in table '{name_table}'. Returning 0.[/bold yellow]"
58-
)
57+
print(f"[bold yellow]Warning: No valid numeric values found for key '{key}' in table '{name_table}'. Returning 0.[/bold yellow]")
5958
return 0
6059

6160
def log(self, data: dict, name_table: str = "main"):
@@ -161,9 +160,7 @@ def get_dataframe(self, name_table: str = "main"):
161160
try:
162161
# Check if column contains bytes that should be treated as numbers
163162
if df[column].dtype == object and any(isinstance(x, bytes) for x in df[column].dropna()):
164-
df[column] = df[column].apply(
165-
lambda x: int.from_bytes(x, byteorder="little") if isinstance(x, bytes) else x
166-
)
163+
df[column] = df[column].apply(lambda x: int.from_bytes(x, byteorder="little") if isinstance(x, bytes) else x)
167164
continue
168165

169166
numeric_series = pandas.to_numeric(df[column], errors="raise")
@@ -272,6 +269,51 @@ def _write_dummy_data(self, name_table: str = "dummy_table"):
272269
print(f"[bold green]Dummy data inserted successfully into {name_table}![/bold green]")
273270
self.print_header()
274271

272+
def has_plateaued(
273+
self,
274+
key: str,
275+
name_table: str = "main",
276+
window_size: int = 16,
277+
threshold_p: float = 0.05,
278+
threshold_slope: float = 0.03,
279+
):
280+
"""check if the key has plateaued in the past window_size values / has to be statistically significant"""
281+
cursor = self.connect.cursor()
282+
cursor.execute(f"PRAGMA table_info({name_table})")
283+
columns = [col[1] for col in cursor.fetchall()]
284+
285+
if key not in columns:
286+
if self.verbose:
287+
print(f"[bold yellow]Warning: Key '{key}' does not exist in table '{name_table}'. Returning False.[/bold yellow]")
288+
return False
289+
290+
query = f"""
291+
SELECT "{key}"
292+
FROM {name_table}
293+
WHERE "{key}" IS NOT NULL
294+
ORDER BY id DESC
295+
LIMIT {window_size}
296+
"""
297+
298+
cursor.execute(query)
299+
results = cursor.fetchall()
300+
cursor.close()
301+
if not results:
302+
if self.verbose:
303+
print(f"[bold yellow]Warning: No data found for key '{key}' in table '{name_table}'. Returning False.[/bold yellow]")
304+
return False
305+
values = [float(result[0]) for result in results if result[0] is not None][-window_size:]
306+
if len(values) < 8:
307+
if self.verbose:
308+
print(f"[bold yellow]Warning: Not enough valid data points for key '{key}'. Returning False.[/bold yellow]")
309+
return False
310+
x = list(range(len(values)))
311+
slope, _, _, p_value, _ = stats.linregress(x, values)
312+
is_flat = p_value > threshold_p and abs(slope) < threshold_slope
313+
if self.verbose and is_flat:
314+
print(f"[bold green]Values for '{key}' have plateaued (p = {p_value:.4f})[/bold green]")
315+
return is_flat
316+
275317

276318
if __name__ == "__main__":
277319
# logger = MetricDB("default.db", verbose=True)
@@ -290,14 +332,48 @@ def _write_dummy_data(self, name_table: str = "dummy_table"):
290332
# logger.show_last_row()
291333
# logger.on_end()
292334

293-
logger = MetricDB("default.db", verbose=True)
294-
logger.print_header()
295-
# logger.get_moving_average(key="Valid Accuracy Balanced")
335+
# logger = MetricDB("default.db", verbose=True)
336+
# logger.print_header()
337+
# # logger.get_moving_average(key="Valid Accuracy Balanced")
338+
339+
# # ---- [1] Demo numeric encoding issue ----
340+
# logger.log({"age": 25.1})
341+
# logger.log({"name": "Alice"})
342+
# df = logger.get_dataframe()
343+
# # ---- [2] All conversions are handled automatically ----
344+
# print(df)
345+
# logger.on_end()
346+
347+
# Test has_plateaued with synthetic data using known functions
348+
logger = MetricDB("test_plateau.db", verbose=True)
349+
350+
# Test case 1: Logistic function (natural plateau)
351+
# f(x) = L / (1 + e^(-k(x-x0))) where L=1, k=1, x0=5
352+
def logistic(x):
353+
return 1 / (1 + 2.71828 ** (-1 * (x - 5)))
354+
355+
print("\nTesting logistic function plateau:")
356+
for x in range(64):
357+
logger.log({"logistic_metric": logistic(x)})
358+
is_plateau = logger.has_plateaued("logistic_metric")
359+
print(f"Has plateaued: {is_plateau}")
360+
361+
# Test case 2: Exponential decay (asymptotic plateau)
362+
# f(x) = e^(-0.5x)
363+
logger = MetricDB("test_exp.db", verbose=True)
364+
print("\nTesting exponential decay plateau:")
365+
for x in range(64):
366+
logger.log({"exp_metric": 2.71828 ** (-0.5 * x)})
367+
is_plateau = logger.has_plateaued("exp_metric")
368+
print(f"Has plateaued: {is_plateau}")
369+
370+
# Test case 3: Linear function (no plateau)
371+
# f(x) = 0.5x
372+
logger = MetricDB("test_linear.db", verbose=True)
373+
print("\nTesting linear function (should not plateau):")
374+
for x in range(64):
375+
logger.log({"linear_metric": 0.5 * x})
376+
is_plateau = logger.has_plateaued("linear_metric")
377+
print(f"Has plateaued: {is_plateau}")
296378

297-
# ---- [1] Demo numeric encoding issue ----
298-
logger.log({"age": 25.1})
299-
logger.log({"name": "Alice"})
300-
df = logger.get_dataframe()
301-
# ---- [2] All conversions are handled automatically ----
302-
print(df)
303379
logger.on_end()

‎requirements.txt

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
pandas
2-
rich
2+
rich
3+
scipy

‎setup.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
setup(
44
name="MetricDB",
5-
version="0.0.11",
5+
version="0.0.12",
66
author="Don Yin",
77
author_email="Don_Yin@outlook.com",
88
description="A logger based on SQLite3",

0 commit comments

Comments
 (0)
Please sign in to comment.