Skip to content

Commit e73bef7

Browse files
committed
Simplify data windowing
1 parent 395df07 commit e73bef7

1 file changed

Lines changed: 51 additions & 56 deletions

File tree

  • tools/machine-learning/fall_detection/dataset
Lines changed: 51 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
from typing import Iterable
22
import tensorflow as tf
33
import numpy as np
4-
from tensorflow.data import Dataset
54
import polars as pl
65
import pandas as pd
76
from polars._typing import IntoExpr
@@ -24,7 +23,7 @@ def __init__(
2423
group_keys: list[str],
2524
features: Iterable[IntoExpr] | IntoExpr,
2625
) -> None:
27-
self.dataframe = dataframe.drop_nulls()
26+
self.dataframe = dataframe.drop_nulls()[:10000]
2827

2928
self.labeller = PseudoLabeller()
3029
self.features = features
@@ -38,7 +37,6 @@ def __init__(
3837
raise Exception(
3938
f"Null values found in features: {number_of_nulls} null values"
4039
)
41-
4240
groups = (
4341
self.dataframe.select(pl.struct(group_keys).rank("dense") - 1)
4442
.to_series()
@@ -54,63 +52,51 @@ def __init__(
5452

5553
def to_windowed(
5654
self,
57-
stride: int = 1,
5855
control_frequency: float = 83,
5956
window_size: float = 1.5,
6057
window_stride: float = 0.2,
61-
is_state_prediction: bool = False,
58+
label_shift: int = 0,
6259
) -> None:
6360
samples_per_window = int(window_size * control_frequency)
61+
self.samples_per_window = samples_per_window
6462
samples_between_windows = int(window_stride * control_frequency)
65-
windows = (
66-
self.dataframe[::stride]
67-
.group_by("group")
68-
.map_groups(
69-
lambda group: group.with_row_index()
70-
.cast({"index": pl.Int32})
71-
.group_by_dynamic(
72-
index_column="index",
73-
every=f"{samples_between_windows}i",
74-
period=f"{samples_per_window}i",
75-
)
76-
.agg([*self.features, pl.col("labels")])
77-
.drop("index")
78-
.filter(self.features[0].list.len() == samples_per_window)
79-
.select(*self.features, pl.col("labels").list.last())
80-
)
63+
64+
windowed_features = pl.concat(
65+
[
66+
self.dataframe.select(
67+
generate_lags(feature, samples_per_window, "group"),
68+
)[
69+
samples_per_window : -label_shift
70+
or None : samples_between_windows
71+
]
72+
for feature in self.features
73+
],
74+
how="horizontal",
8175
)
82-
lowest_label_count = windows.select(
83-
pl.min_horizontal(
84-
pl.col("labels")
85-
.filter(pl.col("labels") == 0)
86-
.len()
87-
.alias("num_upright"),
88-
pl.col("labels")
89-
.filter(pl.col("labels") == 1)
90-
.len()
91-
.alias("num_falling"),
92-
pl.col("labels")
93-
.filter(pl.col("labels") == 2)
94-
.len()
95-
.alias("num_fallen"),
96-
)
97-
).item()
98-
balanced_windows = pl.concat(
99-
(
100-
windows.filter(pl.col("labels") == 0).sample(
101-
n=lowest_label_count
102-
),
103-
windows.filter(pl.col("labels") == 1).sample(
104-
n=lowest_label_count
105-
),
106-
windows.filter(pl.col("labels") == 2).sample(
107-
n=lowest_label_count
108-
),
109-
)
76+
77+
shifted_labels = self.dataframe.select(
78+
pl.col("labels").shift(-label_shift).over("group")
79+
)[samples_per_window : -label_shift or None : samples_between_windows]
80+
81+
windowed_dataframe = windowed_features.hstack(shifted_labels)
82+
83+
n_minority = (
84+
windowed_dataframe.get_column("labels")
85+
.value_counts()
86+
.min()
87+
.select(pl.col("count"))
88+
.item()
89+
)
90+
91+
balanced_df = windowed_dataframe.group_by(
92+
"labels", maintain_order=True
93+
).map_groups(lambda group: group.sample(n=n_minority, seed=1))
94+
balanced_df = windowed_dataframe.sample(
95+
fraction=1, shuffle=True, seed=1
11096
)
111-
balanced_windows = balanced_windows.sample(fraction=1, shuffle=True)
112-
self.input_data = balanced_windows.select(self.features)
113-
self.labels = balanced_windows.select(pl.col("labels"))
97+
98+
self.input_data = balanced_df.drop("labels")
99+
self.labels = balanced_df.select(pl.col("labels"))
114100

115101
def __len__(self) -> int:
116102
return self.groups.unique().numel()
@@ -126,13 +112,18 @@ def __getitem__(self, index: int) -> tuple[tf.Tensor, tf.Tensor]:
126112
return self.input_data[mask], self.labels[mask]
127113

128114
def get_input_tensor(self) -> tf.Tensor:
129-
pd_df = self.input_data.to_pandas()
115+
num_windows = len(self.input_data)
116+
window_length = self.samples_per_window
117+
num_features = len(self.features)
130118
return tf.convert_to_tensor(
131-
np.array(pd_df.values.tolist()), dtype=tf.float32
119+
np.stack([self.input_data.to_numpy()]).reshape(
120+
(num_windows, window_length, num_features)
121+
),
122+
dtype=tf.float32,
132123
)
133124

134125
def get_labels_tensor(self) -> tf.Tensor:
135-
return tf.convert_to_tensor(self.labels.to_numpy())
126+
return tf.convert_to_tensor(self.labels.to_numpy(), dtype=tf.float32)
136127

137128
def get_windows_input_tensor(self) -> list[tf.Tensor]:
138129
windowed_input_tensor = []
@@ -142,4 +133,8 @@ def get_windows_input_tensor(self) -> list[tf.Tensor]:
142133
)
143134
return windowed_input_tensor
144135

145-
# def element_spec()
136+
137+
def generate_lags(feature: pl.Expr, lags: int, group: str) -> list[pl.Expr]:
138+
return [
139+
feature.shift(i).over(group).name.suffix(str(i)) for i in range(lags)
140+
]

0 commit comments

Comments
 (0)