Battery Data
Introduction
Battery data can be recorded by many devices, including smartphones and watches. Battery information can provide insight about activity and sleep patterns. It is also necessary to interpret other data recorded by the device.
Battery data includes information about when the device is shut down, if applicable.
Battery data is expected to have the following columns (column names can be different, but in that case they must be provided as parameters):
user
: Subject IDdevice
: Device IDbattery_level
: The charge level if the device as percentage of full charge.battery_status
: Integer indicator of device status. Fully documented at https://awareframework.com/battery/.Status
2
: chargingStatus
3
: dischargingStatus
5
: battery fullStatus
4
: not chargingStatus
1
: unknownStatus
-1
: shutdownStatus
-2
: rebooted
In this notebook, we will extract battery data from the Aware platform and infer users’ behavioral patterns from their interaction with the phone. The below functions will be described in this notebook:
niimpy.preprocessing.battery.battery_shutdown_info
: returns the timestamp when the device is shutdown or rebootedniimpy.preprocessing.battery.battery_occurrences
: returns the number of battery samples within a time rangeniimpy.preprocessing.battery.battery_gaps
: returns the time gaps between two battery sample
Read data
[1]:
import niimpy
import niimpy.preprocessing.battery as battery
from niimpy import config
import warnings
warnings.filterwarnings("ignore")
/u/24/rantahj1/unix/miniconda3/envs/niimpy/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm
[2]:
data = niimpy.read_csv(config.MULTIUSER_AWARE_BATTERY_PATH, tz='Europe/Helsinki')
data.head()
[2]:
user | device | time | battery_level | battery_status | battery_health | battery_adaptor | datetime | |
---|---|---|---|---|---|---|---|---|
2020-01-09 02:20:02.924999952+02:00 | jd9INuQ5BBlW | 3p83yASkOb_B | 1.578529e+09 | 74 | 3 | 2 | 0 | 2020-01-09 02:20:02.924999952+02:00 |
2020-01-09 02:21:30.405999899+02:00 | jd9INuQ5BBlW | 3p83yASkOb_B | 1.578529e+09 | 73 | 3 | 2 | 0 | 2020-01-09 02:21:30.405999899+02:00 |
2020-01-09 02:24:12.805999994+02:00 | jd9INuQ5BBlW | 3p83yASkOb_B | 1.578529e+09 | 72 | 3 | 2 | 0 | 2020-01-09 02:24:12.805999994+02:00 |
2020-01-09 02:35:38.561000109+02:00 | jd9INuQ5BBlW | 3p83yASkOb_B | 1.578530e+09 | 72 | 2 | 2 | 0 | 2020-01-09 02:35:38.561000109+02:00 |
2020-01-09 02:35:38.953000069+02:00 | jd9INuQ5BBlW | 3p83yASkOb_B | 1.578530e+09 | 72 | 2 | 2 | 2 | 2020-01-09 02:35:38.953000069+02:00 |
Feature extraction
By default, Niimpy data should be ordered by the timestamp in ascending order. We start by sorting the data to make sure it’s compatible.
[3]:
data = data.sort_index()
Next, we will use Niimpy to extract features from the data. These are useful for inspecting the data and can be part of a full analysis workflow.
Usin the battery_occurrences
function, we can count the amount the battery samples every 10 minutes. This function requires the index to be sorted.
[4]:
battery.battery_occurrences(data, resample_args = {"rule": "10min"})
[4]:
device | user | occurrences | |
---|---|---|---|
2020-01-09 02:20:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 3 |
2020-01-09 02:30:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 5 |
2020-01-09 02:40:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 6 |
2020-01-09 02:50:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 6 |
2020-01-09 03:00:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 5 |
... | ... | ... | ... |
2019-08-08 23:50:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 6 |
2019-08-09 00:00:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 6 |
2019-08-09 00:10:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 6 |
2019-08-09 00:20:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 6 |
2019-08-09 00:30:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 4 |
1169 rows × 3 columns
The above dataframe gives the battery information of all users. You can also get the information for an individual by passing a filtered dataframe.
[5]:
f = niimpy.preprocessing.battery.battery_occurrences
data_filtered = data.query('user == "jd9INuQ5BBlW"')
individual_occurrences = battery.extract_features_battery(data_filtered, features={f: {"resample_args": {"rule": "10min"}}})
individual_occurrences.head()
<function battery_occurrences at 0x734b8b358ea0> {'resample_args': {'rule': '10min'}}
[5]:
device | user | occurrences | |
---|---|---|---|
2020-01-09 02:20:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 3 |
2020-01-09 02:30:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 5 |
2020-01-09 02:40:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 6 |
2020-01-09 02:50:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 6 |
2020-01-09 03:00:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 5 |
Next, you can extract the gaps between two consecutive battery samples with the battery_gaps
function.
[6]:
f = niimpy.preprocessing.battery.battery_gaps
gaps = battery.battery_gaps(data)
gaps
[6]:
device | user | battery_gap | |
---|---|---|---|
2020-01-09 02:00:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 0 days 00:01:23.293666680 |
2020-01-09 02:30:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 0 days 00:02:03.573882355 |
2020-01-09 03:00:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 0 days 00:02:09.004461526 |
2020-01-09 03:30:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 0 days 00:02:09.319333346 |
2020-01-09 04:00:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 0 days 00:00:39.763869565 |
... | ... | ... | ... |
2019-08-08 22:30:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 0 days 00:02:21.165571434 |
2019-08-08 23:00:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 0 days 00:01:35.393473687 |
2019-08-08 23:30:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 0 days 00:01:38.987111104 |
2019-08-09 00:00:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 0 days 00:01:40.123666670 |
2019-08-09 00:30:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 0 days 00:01:42.400250017 |
393 rows × 3 columns
Knowing when the phone is shutdown is essential if we want to infer the usage behaviour of the subjects. This can be done by calling the shutdown_info
function. The function returns the timestamp when the phone is shut down or rebooted (e.g: battery_status = -1).
[7]:
shutdown = battery.shutdown_info(data, battery_column_name = 'battery_status')
shutdown
[7]:
user | device | time | battery_level | battery_status | battery_health | battery_adaptor | datetime | |
---|---|---|---|---|---|---|---|---|
2019-08-07 10:37:11.308000088+03:00 | dvWdLQesv21a | i8jmoIuoe12Mo | 1.565163e+09 | 2 | -1 | 2 | 0 | 2019-08-07 10:37:11.308000088+03:00 |
2019-08-07 10:37:11.308000088+03:00 | iGyXetHE3S8u | Cq9vueHh3zVs | 1.565163e+09 | 2 | -1 | 2 | 0 | 2019-08-07 10:37:11.308000088+03:00 |
2019-08-07 10:37:11.322999954+03:00 | dvWdLQesv21a | i8jmoIuoe12Mo | 1.565163e+09 | 2 | -1 | 2 | 0 | 2019-08-07 10:37:11.322999954+03:00 |
2019-08-07 10:37:11.322999954+03:00 | iGyXetHE3S8u | Cq9vueHh3zVs | 1.565163e+09 | 2 | -1 | 2 | 0 | 2019-08-07 10:37:11.322999954+03:00 |
Extracting features with the extract_features call
We have seen above how to extract battery features using niimpy
. Sometimes, we need more than one features and it would be inconvenient to extract everything one by one. niimpy
provides a extract_feature
call to allow you extracting all the features available and combining them into a single data frame. The extractable features must start with the prefix battery_
.
[ ]:
# Start by defining the feature name
f0 = niimpy.preprocessing.battery.battery_occurrences
f1 = niimpy.preprocessing.battery.battery_gaps
f2 = niimpy.preprocessing.battery.battery_charge_discharge
# The extract_feature function requires a features parameter.
# This parameter accepts a dictionary where the key is the feature name and value
# is a dictionary containing values passed to the function.
features = battery.extract_features_battery(
data,
features={
f0: {'rule': "10min"},
f1: {},
f2: {}
}
)
features.head()
<function battery_occurrences at 0x734b8b358ea0> {'rule': '10min'}
<function battery_gaps at 0x734b8b358f40> {}
<function battery_charge_discharge at 0x734b8b358fe0> {}
device | user | occurrences | battery_gap | charge/discharge | bdelta | |
---|---|---|---|---|---|---|
2020-01-09 02:00:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 3 | 0 days 00:01:23.293666680 | -0.008794 | -0.666667 |
2020-01-09 02:30:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 17 | 0 days 00:02:03.573882355 | 0.014987 | 0.764706 |
2020-01-09 03:00:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 13 | 0 days 00:02:09.004461526 | 0.008331 | 1.000000 |
2020-01-09 03:30:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 15 | 0 days 00:02:09.319333346 | 0.034008 | 0.133333 |
2020-01-09 04:00:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 46 | 0 days 00:00:39.763869565 | 0.000000 | 0.000000 |
[9]:
f1(data)
[9]:
device | user | battery_gap | |
---|---|---|---|
2020-01-09 02:00:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 0 days 00:01:23.293666680 |
2020-01-09 02:30:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 0 days 00:02:03.573882355 |
2020-01-09 03:00:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 0 days 00:02:09.004461526 |
2020-01-09 03:30:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 0 days 00:02:09.319333346 |
2020-01-09 04:00:00+02:00 | 3p83yASkOb_B | jd9INuQ5BBlW | 0 days 00:00:39.763869565 |
... | ... | ... | ... |
2019-08-08 22:30:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 0 days 00:02:21.165571434 |
2019-08-08 23:00:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 0 days 00:01:35.393473687 |
2019-08-08 23:30:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 0 days 00:01:38.987111104 |
2019-08-09 00:00:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 0 days 00:01:40.123666670 |
2019-08-09 00:30:00+03:00 | i8jmoIuoe12Mo | dvWdLQesv21a | 0 days 00:01:42.400250017 |
393 rows × 3 columns