File size: 7,268 Bytes
60b21d3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# SPDX-FileCopyrightText: 2025 Stanford University, ETH Zurich, and the project authors (see CONTRIBUTORS.md)
# SPDX-FileCopyrightText: 2025 This source file is part of the OpenTSLM open-source project.
#
# SPDX-License-Identifier: MIT

from typing import Tuple
import pandas as pd
from opentslm.time_series_datasets.pamap2.pamap2_loader import ensure_pamap2_data
from torch.utils.data import Dataset

ACTIVITIY_ID_DICT = {
    0: "transient",
    1: "lying",
    2: "sitting",
    3: "standing",
    4: "walking",
    5: "running",
    6: "cycling",
    7: "nordic walking",
    9: "watching TV",
    10: "computer work",
    11: "car driving",
    12: "ascending stairs",
    13: "descending stairs",
    16: "vacuum cleaning",
    17: "ironing",
    18: "folding laundry",
    19: "house cleaning",
    20: "playing soccer",
    24: "rope jumping",
}


class PAMAP2Dataset(Dataset):
    # source: https://github.com/andreasKyratzis/PAMAP2-Physical-Activity-Monitoring-Data-Analysis-and-ML/blob/master/pamap2.ipynb
    def _data_cleaning(self, dataCollection):
        dataCollection = dataCollection.drop(
            [
                "handOrientation1",
                "handOrientation2",
                "handOrientation3",
                "handOrientation4",
                "chestOrientation1",
                "chestOrientation2",
                "chestOrientation3",
                "chestOrientation4",
                "ankleOrientation1",
                "ankleOrientation2",
                "ankleOrientation3",
                "ankleOrientation4",
            ],
            axis=1,
        )  # removal of orientation columns as they are not needed
        dataCollection = dataCollection.drop(
            dataCollection[dataCollection.activityID == 0].index
        )  # removal of any row of activity 0 as it is transient activity which it is not used
        dataCollection = dataCollection.apply(
            pd.to_numeric, errors="coerce"
        )  # removal of non numeric data in cells
        dataCollection = dataCollection.interpolate()  # removal of any remaining NaN value cells by constructing new data points in known set of data points

        return dataCollection

    def _load_data(self, list_of_files):
        # Load data
        ensure_pamap2_data()

        colNames = ["timestamp", "activityID", "heartrate"]
        IMUhand = [
            "handTemperature",
            "handAcc16_1",
            "handAcc16_2",
            "handAcc16_3",
            "handAcc6_1",
            "handAcc6_2",
            "handAcc6_3",
            "handGyro1",
            "handGyro2",
            "handGyro3",
            "handMagne1",
            "handMagne2",
            "handMagne3",
            "handOrientation1",
            "handOrientation2",
            "handOrientation3",
            "handOrientation4",
        ]

        IMUchest = [
            "chestTemperature",
            "chestAcc16_1",
            "chestAcc16_2",
            "chestAcc16_3",
            "chestAcc6_1",
            "chestAcc6_2",
            "chestAcc6_3",
            "chestGyro1",
            "chestGyro2",
            "chestGyro3",
            "chestMagne1",
            "chestMagne2",
            "chestMagne3",
            "chestOrientation1",
            "chestOrientation2",
            "chestOrientation3",
            "chestOrientation4",
        ]

        IMUankle = [
            "ankleTemperature",
            "ankleAcc16_1",
            "ankleAcc16_2",
            "ankleAcc16_3",
            "ankleAcc6_1",
            "ankleAcc6_2",
            "ankleAcc6_3",
            "ankleGyro1",
            "ankleGyro2",
            "ankleGyro3",
            "ankleMagne1",
            "ankleMagne2",
            "ankleMagne3",
            "ankleOrientation1",
            "ankleOrientation2",
            "ankleOrientation3",
            "ankleOrientation4",
        ]

        columns = colNames + IMUhand + IMUchest + IMUankle  # all columns in one list
        dataCollection = pd.DataFrame()
        for file in list_of_files:
            procData = pd.read_table(file, header=None, sep=r"\s+")
            procData.columns = columns
            procData["subject_id"] = int(file[-5])
            dataCollection = pd.concat([dataCollection, procData], ignore_index=True)

        dataCollection.reset_index(drop=True, inplace=True)
        dataCol = self._data_cleaning(dataCollection)
        dataCol.reset_index(drop=True, inplace=True)
        for i in range(0, 4):
            dataCol.loc[i, "heartrate"] = 100
        dataCol["activityID"] = dataCol["activityID"].map(ACTIVITIY_ID_DICT)
        return dataCol

    def __init__(self, list_of_files):
        super().__init__()
        self.df = self._load_data(list_of_files)

        # create 3-second windows and store them as tensors + labels
        self.time_series, self.labels = self._make_windows(
            window_size="3s", min_pct=0.5
        )

    def _make_windows(self, window_size, min_pct=0.5):
        """
        Returns:
          windows: list of numpy arrays of shape (n_features, n_steps)
          labels : list of activity labels (the mode of each window)
        Drops any window whose modal activity < min_pct of that window's rows,
        and only ever groups rows from the same subject.
        """
        # 1) copy & timestamp → datetime → index
        df = self.df.copy()
        df["timestamp"] = pd.to_datetime(df["timestamp"], unit="s")
        df = df.set_index("timestamp")

        # 2) define which columns are features (everything except label & subject_id)
        feature_cols = df.columns.drop(["activityID"])

        windows = []
        labels = []

        # 3) first split by subject_id so no window can span subjects
        for subject, df_sub in df.groupby("subject_id"):
            # align your 2‑minute bins to this subject’s first timestamp
            origin = df_sub.index[0]
            for window_start, win in df_sub.resample(window_size, origin=origin):
                if win.empty:
                    continue

                # 4) find most common activity in this window
                mode_ser = win["activityID"].mode()
                if mode_ser.empty:
                    continue
                mode = mode_ser.iloc[0]

                # 5) check that it covers at least min_pct of the rows
                if (win["activityID"] == mode).sum() < min_pct * len(win):
                    continue

                # 6) store the **transposed** feature‑array + label
                #    now the shape is (n_features, n_steps)

                ts_dict = {col: win[col].values for col in feature_cols}
                windows.append(ts_dict)
                labels.append(mode)

        return windows, labels

    def __len__(self):
        # now based on windows, not raw rows
        return len(self.time_series)

    def __getitem__(self, idx):
        # returns (n_steps × n_features array, activity string)
        return {"time_series": self.time_series[idx], "label": self.labels[idx]}


if __name__ == "__main__":
    dataset = PAMAP2Dataset()

    for data_point in dataset:
        window = data_point["time_series"]
        label = data_point["label"]
        print(f"{window}, {label}")