diff --git a/Snakefile b/Snakefile index e1937d0d..7cac0462 100644 --- a/Snakefile +++ b/Snakefile @@ -22,9 +22,11 @@ rule all: pid=config["PIDS"], segment = config["BLUETOOTH"]["DAY_SEGMENTS"]), expand("data/processed/{pid}/google_activity_recognition.csv",pid=config["PIDS"]), + expand("data/processed/{pid}/battery_daily.csv", pid=config["PIDS"]), # Reports expand("reports/figures/{pid}/{sensor}_heatmap_rows.html", pid=config["PIDS"], sensor=config["SENSORS"]), expand("reports/figures/{pid}/compliance_heatmap.html", pid=config["PIDS"], sensor=config["SENSORS"]), + expand("reports/figures/{pid}/battery_consumption_rates_barchart.html", pid=config["PIDS"]), # --- Packrat Rules --- # ## Taken from https://github.com/lachlandeer/snakemake-econ-r diff --git a/rules/features.snakefile b/rules/features.snakefile index 0edbfc81..d404959a 100644 --- a/rules/features.snakefile +++ b/rules/features.snakefile @@ -59,3 +59,11 @@ rule activity_metrics: "data/processed/{pid}/google_activity_recognition.csv" script: "../src/features/google_activity_recognition.py" + +rule battery_metrics: + input: + "data/processed/{pid}/battery_deltas.csv" + output: + "data/processed/{pid}/battery_daily.csv" + script: + "../src/features/battery_metrics.py" diff --git a/rules/reports.snakefile b/rules/reports.snakefile index 75df3f72..ad045b4b 100644 --- a/rules/reports.snakefile +++ b/rules/reports.snakefile @@ -18,3 +18,13 @@ rule compliance_heatmap: "reports/figures/{pid}/compliance_heatmap.html" script: "../src/visualization/compliance_heatmap.py" + +rule battery_consumption_rates_barchart: + input: + "data/processed/{pid}/battery_daily.csv" + params: + pid = "{pid}" + output: + "reports/figures/{pid}/battery_consumption_rates_barchart.html" + script: + "../src/visualization/battery_consumption_rates_barchart.py" diff --git a/src/features/battery_metrics.py b/src/features/battery_metrics.py new file mode 100644 index 00000000..db6d6be9 --- /dev/null +++ b/src/features/battery_metrics.py @@ -0,0 +1,60 @@ +import pandas as pd +import datetime + + +battery_data = pd.read_csv(snakemake.input[0]) +if battery_data.empty: + battery_features = pd.DataFrame(columns=["battery_diff", "time_diff", "battery_decrease_times","battery_consumption_rate", "local_date"]) +else: + for col in ["local_start_date_time", "local_end_date_time", "local_start_date", "local_end_date"]: + battery_data[col] = pd.to_datetime(battery_data[col]) + + # split the row into 2 rows when local_start_date + 1 = local_end_date + battery_data_overnight = battery_data[battery_data["local_start_date"] + datetime.timedelta(days=1) == battery_data["local_end_date"]] + if not battery_data_overnight.empty: + battery_data_overnight_first, battery_data_overnight_second = pd.DataFrame(columns=battery_data.columns), pd.DataFrame(columns=battery_data.columns) + battery_data_overnight_first["total_battery_diff"], battery_data_overnight_second["total_battery_diff"] = battery_data_overnight["battery_diff"], battery_data_overnight["battery_diff"] + battery_data_overnight_first["total_time_diff"], battery_data_overnight_second["total_time_diff"] = battery_data_overnight["time_diff"], battery_data_overnight["time_diff"] + # let start = start OR end = end, then fill the left col with the start+23:59:59. + battery_data_overnight_first["local_start_date_time"] = battery_data_overnight["local_start_date_time"] + battery_data_overnight_first["local_end_date_time"] = battery_data_overnight["local_start_date"].apply(lambda x: datetime.datetime.combine(x, datetime.time(23,59,59))) + battery_data_overnight_first["local_start_date"] = battery_data_overnight["local_start_date"] + battery_data_overnight_second["local_end_date_time"] = battery_data_overnight["local_end_date_time"] + battery_data_overnight_second["local_start_date_time"] = battery_data_overnight["local_start_date"].apply(lambda x: datetime.datetime.combine(x, datetime.time(23,59,59))) + battery_data_overnight_second["local_start_date"] = battery_data_overnight["local_end_date"] + battery_data_overnight = pd.concat([battery_data_overnight_first, battery_data_overnight_second]) + # calculate battery_diff and time_diff + battery_data_overnight["time_diff"] = (battery_data_overnight["local_end_date_time"]-battery_data_overnight["local_start_date_time"]).apply(lambda x: x.total_seconds()/3600) + battery_data_overnight["battery_diff"] = battery_data_overnight["total_battery_diff"]*(battery_data_overnight["time_diff"]/battery_data_overnight["total_time_diff"]) + del battery_data_overnight["total_battery_diff"], battery_data_overnight["total_time_diff"] + + # filter out the rows when local_start_date + 1 < local_end_date + battery_data = battery_data[battery_data["local_start_date"] == battery_data["local_end_date"]] + + # combine + battery_data = pd.concat([battery_data, battery_data_overnight]) + + # split into decrease table and charge table + battery_data_decrease = battery_data[battery_data["battery_diff"] > 0] + battery_data_charge = battery_data[battery_data["battery_diff"] <= 0] + + # for battery_data_decrease: + battery_decrease_count = battery_data_decrease.groupby(["local_start_date"])["local_start_date"].count() + battery_data_decrease = battery_data_decrease.groupby(["local_start_date"]).sum() + battery_data_decrease["battery_decrease_count"] = battery_decrease_count + battery_data_decrease["battery_decrease_duration"] = battery_data_decrease["time_diff"] + battery_data_decrease["battery_consumption_rate"] = battery_data_decrease["battery_diff"]/battery_data_decrease["time_diff"] + del battery_data_decrease["battery_diff"], battery_data_decrease["time_diff"] + + # for battery_data_charge: + battery_charge_count = battery_data_charge.groupby(["local_start_date"])["local_start_date"].count() + battery_data_charge = battery_data_charge.groupby(["local_start_date"]).sum() + battery_data_charge["battery_charge_count"] = battery_charge_count + battery_data_charge["battery_charge_duration"] = battery_data_charge["time_diff"] + del battery_data_charge["battery_diff"], battery_data_charge["time_diff"] + + # combine decrease features and charge features + battery_features = pd.concat([battery_data_decrease, battery_data_charge], axis=1, sort=True) + battery_features["local_date"] = battery_features.index + battery_features.reset_index(inplace=True, drop=True) +battery_features.to_csv(snakemake.output[0], index=False) \ No newline at end of file diff --git a/src/visualization/battery_consumption_rates_barchart.py b/src/visualization/battery_consumption_rates_barchart.py new file mode 100644 index 00000000..6511035e --- /dev/null +++ b/src/visualization/battery_consumption_rates_barchart.py @@ -0,0 +1,26 @@ +import pandas as pd +import datetime +import plotly.io as pio +import plotly.graph_objects as go + +def getBatteryConsumptionRatesBarChart(battery_data, pid): + plot = go.Figure(go.Bar( + x=battery_data["battery_consumption_rate"], + y=battery_data["local_date"].apply(lambda x: x.replace("-","/")).tolist(), + orientation='h')) + plot.update_layout(title="Daily battery consumption rates bar chart for " + pid, + xaxis_title="battery drains % per hour", + ) + return plot + + + +battery_data = pd.read_csv(snakemake.input[0]) +pid = snakemake.params["pid"] +if battery_data.empty: + empty_html = open(snakemake.output[0], "w") + empty_html.write("There is no "+ sensor_name + " data for "+pid) + empty_html.close() +else: + plot = getBatteryConsumptionRatesBarChart(battery_data, pid) + pio.write_html(plot, file=snakemake.output[0], auto_open=False) \ No newline at end of file