actinet 0.0.dev4__tar.gz → 0.0.dev6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. {actinet-0.0.dev4 → actinet-0.0.dev6}/PKG-INFO +36 -14
  2. {actinet-0.0.dev4 → actinet-0.0.dev6}/README.md +35 -13
  3. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet/__init__.py +2 -2
  4. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet/_version.py +3 -3
  5. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet/actinet.py +94 -59
  6. actinet-0.0.dev6/src/actinet/evaluate.py +215 -0
  7. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet/hmm.py +14 -11
  8. actinet-0.0.dev6/src/actinet/models.py +341 -0
  9. actinet-0.0.dev6/src/actinet/prepare.py +329 -0
  10. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet/sslmodel.py +136 -0
  11. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet/summarisation.py +25 -32
  12. actinet-0.0.dev6/src/actinet/utils/utils.py +89 -0
  13. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet.egg-info/PKG-INFO +36 -14
  14. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet.egg-info/SOURCES.txt +2 -0
  15. actinet-0.0.dev4/src/actinet/models.py +0 -187
  16. actinet-0.0.dev4/src/actinet/utils/utils.py +0 -36
  17. {actinet-0.0.dev4 → actinet-0.0.dev6}/LICENSE.md +0 -0
  18. {actinet-0.0.dev4 → actinet-0.0.dev6}/pyproject.toml +0 -0
  19. {actinet-0.0.dev4 → actinet-0.0.dev6}/setup.cfg +0 -0
  20. {actinet-0.0.dev4 → actinet-0.0.dev6}/setup.py +0 -0
  21. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet/accPlot.py +0 -0
  22. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet/circadian.py +0 -0
  23. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet/utils/__init__.py +0 -0
  24. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet/utils/collate_outputs.py +0 -0
  25. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet/utils/generate_commands.py +0 -0
  26. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet.egg-info/dependency_links.txt +0 -0
  27. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet.egg-info/entry_points.txt +0 -0
  28. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet.egg-info/requires.txt +0 -0
  29. {actinet-0.0.dev4 → actinet-0.0.dev6}/src/actinet.egg-info/top_level.txt +0 -0
  30. {actinet-0.0.dev4 → actinet-0.0.dev6}/versioneer.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: actinet
3
- Version: 0.0.dev4
3
+ Version: 0.0.dev6
4
4
  Summary: Activity detection algorithm compatible with the UK Biobank Accelerometer Dataset
5
5
  Home-page: https://github.com/OxWearables/actinet
6
6
  Download-URL: https://github.com/OxWearables/actinet
@@ -60,16 +60,16 @@ You are all set! The next time that you want to use `actinet`, open the Anaconda
60
60
 
61
61
  ```bash
62
62
  # Process an AX3 file
63
- $ actinet sample.cwa
63
+ $ actinet -f sample.cwa
64
64
 
65
65
  # Or an ActiGraph file
66
- $ actinet sample.gt3x
66
+ $ actinet -f sample.gt3x
67
67
 
68
68
  # Or a GENEActiv file
69
- $ actinet sample.bin
69
+ $ actinet -f sample.bin
70
70
 
71
71
  # Or a CSV file (see data format below)
72
- $ actinet sample.csv
72
+ $ actinet -f sample.csv
73
73
  ```
74
74
 
75
75
  ### Troubleshooting
@@ -77,7 +77,29 @@ $ actinet sample.csv
77
77
  Some systems may face issues with Java when running the script. If this is your case, try fixing OpenJDK to version 8:
78
78
 
79
79
  ```console
80
- conda install -n actinet openjdk=8
80
+ conda create -n actinet openjdk=8
81
+ ```
82
+
83
+ ### Offline usage
84
+
85
+ To use this package offline, one must first download and install the relevant classifier file and model modules.
86
+ This repository offers two ways of doing this.
87
+
88
+ Run the following code when you have internet access:
89
+ ```console
90
+ actinet --cache-classifier
91
+ ```
92
+
93
+ Following this, the actinet classifier can be used as standard without internet access, without needing to specify the flags relating to the model repository.
94
+
95
+ Alternatively, you can download or git clone the ssl modules from the [ssl-wearables repository](https://github.com/OxWearables/ssl-wearables).
96
+
97
+ In addition, you can donwload/prepare a custom classifier file.
98
+
99
+ Once this is downloaded to an appopriate location, you can run the actinet model using:
100
+
101
+ ```console
102
+ actinet -f sample.cwa -c /path/to/classifier.joblib.lzma -m /path/to/ssl-wearables
81
103
  ```
82
104
 
83
105
  ### Output files
@@ -85,7 +107,7 @@ conda install -n actinet openjdk=8
85
107
  By default, output files will be stored in a folder named after the input file, `outputs/{filename}/`, created in the current working directory. You can change the output path with the `-o` flag:
86
108
 
87
109
  ```console
88
- $ actinet sample.cwa -o /path/to/some/folder/
110
+ $ actinet -f sample.cwa -o /path/to/some/folder/
89
111
 
90
112
  <Output summary written to: /path/to/some/folder/sample-outputSummary.json>
91
113
  <Time series output written to: /path/to/some/folder/sample-timeSeries.csv.gz>
@@ -103,7 +125,7 @@ See [Data Dictionary](https://biobankaccanalysis.readthedocs.io/en/latest/datadi
103
125
  To plot the activity profiles, you can use the -p flag:
104
126
 
105
127
  ```console
106
- $ actinet sample.cwa -p
128
+ $ actinet -f sample.cwa -p
107
129
  <Output plot written to: data/sample-timeSeries-plot.png>
108
130
  ```
109
131
 
@@ -138,9 +160,9 @@ To process multiple files you can create a text file in Notepad which includes o
138
160
  Example text file *commands.txt*:
139
161
 
140
162
  ```console
141
- actinet file1.cwa &
142
- actinet file2.cwa &
143
- actinet file3.cwa
163
+ actinet -f file1.cwa &
164
+ actinet -f file2.cwa &
165
+ actinet -f file3.cwa
144
166
  :END
145
167
  ````
146
168
 
@@ -151,9 +173,9 @@ Once this file is created, run `cmd < commands.txt` from the terminal.
151
173
  Create a file *command.sh* with:
152
174
 
153
175
  ```console
154
- actinet file1.cwa
155
- actinet file2.cwa
156
- actinet file3.cwa
176
+ actinet -f file1.cwa
177
+ actinet -f file2.cwa
178
+ actinet -f file3.cwa
157
179
  ```
158
180
 
159
181
  Then, run `bash command.sh` from the terminal.
@@ -38,16 +38,16 @@ You are all set! The next time that you want to use `actinet`, open the Anaconda
38
38
 
39
39
  ```bash
40
40
  # Process an AX3 file
41
- $ actinet sample.cwa
41
+ $ actinet -f sample.cwa
42
42
 
43
43
  # Or an ActiGraph file
44
- $ actinet sample.gt3x
44
+ $ actinet -f sample.gt3x
45
45
 
46
46
  # Or a GENEActiv file
47
- $ actinet sample.bin
47
+ $ actinet -f sample.bin
48
48
 
49
49
  # Or a CSV file (see data format below)
50
- $ actinet sample.csv
50
+ $ actinet -f sample.csv
51
51
  ```
52
52
 
53
53
  ### Troubleshooting
@@ -55,7 +55,29 @@ $ actinet sample.csv
55
55
  Some systems may face issues with Java when running the script. If this is your case, try fixing OpenJDK to version 8:
56
56
 
57
57
  ```console
58
- conda install -n actinet openjdk=8
58
+ conda create -n actinet openjdk=8
59
+ ```
60
+
61
+ ### Offline usage
62
+
63
+ To use this package offline, one must first download and install the relevant classifier file and model modules.
64
+ This repository offers two ways of doing this.
65
+
66
+ Run the following code when you have internet access:
67
+ ```console
68
+ actinet --cache-classifier
69
+ ```
70
+
71
+ Following this, the actinet classifier can be used as standard without internet access, without needing to specify the flags relating to the model repository.
72
+
73
+ Alternatively, you can download or git clone the ssl modules from the [ssl-wearables repository](https://github.com/OxWearables/ssl-wearables).
74
+
75
+ In addition, you can donwload/prepare a custom classifier file.
76
+
77
+ Once this is downloaded to an appopriate location, you can run the actinet model using:
78
+
79
+ ```console
80
+ actinet -f sample.cwa -c /path/to/classifier.joblib.lzma -m /path/to/ssl-wearables
59
81
  ```
60
82
 
61
83
  ### Output files
@@ -63,7 +85,7 @@ conda install -n actinet openjdk=8
63
85
  By default, output files will be stored in a folder named after the input file, `outputs/{filename}/`, created in the current working directory. You can change the output path with the `-o` flag:
64
86
 
65
87
  ```console
66
- $ actinet sample.cwa -o /path/to/some/folder/
88
+ $ actinet -f sample.cwa -o /path/to/some/folder/
67
89
 
68
90
  <Output summary written to: /path/to/some/folder/sample-outputSummary.json>
69
91
  <Time series output written to: /path/to/some/folder/sample-timeSeries.csv.gz>
@@ -81,7 +103,7 @@ See [Data Dictionary](https://biobankaccanalysis.readthedocs.io/en/latest/datadi
81
103
  To plot the activity profiles, you can use the -p flag:
82
104
 
83
105
  ```console
84
- $ actinet sample.cwa -p
106
+ $ actinet -f sample.cwa -p
85
107
  <Output plot written to: data/sample-timeSeries-plot.png>
86
108
  ```
87
109
 
@@ -116,9 +138,9 @@ To process multiple files you can create a text file in Notepad which includes o
116
138
  Example text file *commands.txt*:
117
139
 
118
140
  ```console
119
- actinet file1.cwa &
120
- actinet file2.cwa &
121
- actinet file3.cwa
141
+ actinet -f file1.cwa &
142
+ actinet -f file2.cwa &
143
+ actinet -f file3.cwa
122
144
  :END
123
145
  ````
124
146
 
@@ -129,9 +151,9 @@ Once this file is created, run `cmd < commands.txt` from the terminal.
129
151
  Create a file *command.sh* with:
130
152
 
131
153
  ```console
132
- actinet file1.cwa
133
- actinet file2.cwa
134
- actinet file3.cwa
154
+ actinet -f file1.cwa
155
+ actinet -f file2.cwa
156
+ actinet -f file3.cwa
135
157
  ```
136
158
 
137
159
  Then, run `bash command.sh` from the terminal.
@@ -4,8 +4,8 @@ __maintainer__ = "Shing Chan"
4
4
  __maintainer_email__ = "shing.chan@ndph.ox.ac.uk"
5
5
  __license__ = "See LICENSE file."
6
6
 
7
- __model_version__ = "ssl_ukb_c24_rw_20240204"
8
- __model_md5__ = "84f3d5bb73de5c4da057918c45400da4"
7
+ __classifier_version__ = "ssl_ukb_c24_rw_20240204"
8
+ __classifier_md5__ = "11c3f36348dae37da4f99bd6d810bbb2"
9
9
 
10
10
  from . import _version
11
11
 
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2024-02-05T14:38:16+0000",
11
+ "date": "2024-03-09T08:28:09+0000",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "5857a41418deef2d09568018e05ecae250c54d14",
15
- "version": "0.0.dev4"
14
+ "full-revisionid": "5b419d5fe6079975dbc6ccbca13b128c52709f3f",
15
+ "version": "0.0.dev6"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -12,15 +12,14 @@ import joblib
12
12
 
13
13
  import actipy
14
14
 
15
- from actinet import __model_version__
16
- from actinet import __model_md5__
15
+ from actinet import __classifier_version__
16
+ from actinet import __classifier_md5__
17
17
  from actinet.accPlot import plotTimeSeries
18
18
  from actinet.models import ActivityClassifier
19
- from actinet.sslmodel import SAMPLE_RATE
20
- from actinet.summarisation import getActivitySummary, ACTIVITY_LABELS
19
+ from actinet.summarisation import getActivitySummary
21
20
  from actinet.utils.utils import infer_freq
22
21
 
23
- BASE_URL = "https://zenodo.org/records/10619096/files/"
22
+ BASE_URL = "https://zenodo.org/records/10625542/files/"
24
23
 
25
24
 
26
25
  def main():
@@ -37,10 +36,15 @@ def main():
37
36
  default="outputs/",
38
37
  )
39
38
  parser.add_argument(
40
- "--model-path", "-m", help="Enter custom model file to use", default=None
39
+ "--classifier-path",
40
+ "-c",
41
+ help="Enter custom acitivty classifier file to use",
42
+ default=None,
41
43
  )
42
44
  parser.add_argument(
43
- "--force-download", action="store_true", help="Force download of model file"
45
+ "--force-download",
46
+ action="store_true",
47
+ help="Force download of classifier file",
44
48
  )
45
49
  parser.add_argument(
46
50
  "--pytorch-device",
@@ -63,12 +67,12 @@ def main():
63
67
  help="Plot the predicted activity labels",
64
68
  )
65
69
  parser.add_argument(
66
- "--cache-ssl",
70
+ "--cache-classifier",
67
71
  action="store_true",
68
- help="Download and cache ssl module for offline usage",
72
+ help="Download and cache classifier file and model modules for offline usage",
69
73
  )
70
74
  parser.add_argument(
71
- "--ssl-repo-path", "-s", help="Enter repository of ssl model", default=None
75
+ "--model-repo-path", "-m", help="Enter repository of ssl model", default=None
72
76
  )
73
77
  parser.add_argument("--quiet", "-q", action="store_true", help="Suppress output")
74
78
  args = parser.parse_args()
@@ -77,18 +81,32 @@ def main():
77
81
 
78
82
  verbose = not args.quiet
79
83
 
80
- if args.cache_ssl:
81
- model = ActivityClassifier(weights_path=None, ssl_repo=None, verbose=verbose, labels=ACTIVITY_LABELS)
84
+ classifier_path = (
85
+ pathlib.Path(__file__).parent / f"{__classifier_version__}.joblib.lzma"
86
+ )
87
+
88
+ if args.cache_classifier:
89
+ load_classifier(
90
+ classifier_path=classifier_path,
91
+ model_repo_path=None,
92
+ check_md5=True,
93
+ force_download=True,
94
+ verbose=verbose,
95
+ )
82
96
 
83
97
  after = time.time()
84
98
  print(f"Done! ({round(after - before,2)}s)")
85
99
 
86
100
  return
87
101
 
102
+ else:
103
+ if not args.filepath:
104
+ raise ValueError("Please provide a file to process.")
105
+
88
106
  # Load file
89
107
  data, info = read(
90
108
  args.filepath,
91
- resample_hz=SAMPLE_RATE,
109
+ resample_hz=None,
92
110
  sample_rate=args.sample_rate,
93
111
  verbose=verbose,
94
112
  )
@@ -98,21 +116,25 @@ def main():
98
116
  outdir = os.path.join(args.outdir, basename)
99
117
  os.makedirs(outdir, exist_ok=True)
100
118
 
101
- # Run model
119
+ # Run classifier
102
120
  if verbose:
103
- print("Loading model...")
104
- model_path = pathlib.Path(__file__).parent / f"{__model_version__}.joblib.lzma"
105
- check_md5 = args.model_path is None
106
- model: ActivityClassifier = load_model(
107
- args.model_path or model_path, check_md5, args.force_download, verbose
121
+ print("Loading classifier...")
122
+
123
+ check_md5 = args.classifier_path is None
124
+ classifier: ActivityClassifier = load_classifier(
125
+ args.classifier_path or classifier_path,
126
+ args.model_repo_path,
127
+ check_md5,
128
+ args.force_download,
129
+ verbose,
108
130
  )
109
131
 
110
- model.verbose = verbose
111
- model.device = args.pytorch_device
132
+ classifier.verbose = verbose
133
+ classifier.device = args.pytorch_device
112
134
 
113
135
  if verbose:
114
136
  print("Running activity classifier...")
115
- Y = model.predict_from_frame(data)
137
+ Y = classifier.predict_from_frame(data, args.sample_rate)
116
138
 
117
139
  # Save predicted activities
118
140
  timeSeriesFile = f"{outdir}/{basename}-timeSeries.csv.gz"
@@ -121,8 +143,17 @@ def main():
121
143
  if verbose:
122
144
  print("Time series output written to:", timeSeriesFile)
123
145
 
146
+ # Plot activity profile
147
+ if args.plot_activity:
148
+ plotFile = f"{outdir}/{basename}-timeSeries-plot.png"
149
+ fig = plotTimeSeries(Y)
150
+ fig.savefig(plotFile, dpi=200, bbox_inches="tight")
151
+
152
+ if verbose:
153
+ print("Output plot written to:", plotFile)
154
+
124
155
  # Summary
125
- summary = getActivitySummary(Y, True, True, verbose)
156
+ summary = getActivitySummary(Y, list(classifier.labels), True, True, verbose)
126
157
 
127
158
  # Join the actipy processing info, with acitivity summary data
128
159
  outputSummary = {**summary, **info}
@@ -139,34 +170,33 @@ def main():
139
170
  if verbose:
140
171
  print("\nSummary Stats\n---------------------")
141
172
  print(
142
- {
143
- key: outputSummary[key]
144
- for key in [
145
- "Filename",
146
- "Filesize(MB)",
147
- "WearTime(days)",
148
- "NonwearTime(days)",
149
- "ReadOK",
150
- "acc-overall-avg(mg)",
151
- ]
152
- + [f"{label}-week-avg" for label in ACTIVITY_LABELS]
153
- }
173
+ json.dumps(
174
+ {
175
+ key: outputSummary[key]
176
+ for key in [
177
+ "Filename",
178
+ "Filesize(MB)",
179
+ "WearTime(days)",
180
+ "NonwearTime(days)",
181
+ "ReadOK",
182
+ ]
183
+ + [
184
+ f"{label}-overall-avg"
185
+ for label in ["acc"] + list(classifier.labels)
186
+ ]
187
+ },
188
+ indent=4,
189
+ cls=NpEncoder,
190
+ )
154
191
  )
155
192
 
156
- # Plot activity profile
157
- if args.plot_activity:
158
- plotFile = f"{outdir}/{basename}-timeSeries-plot.png"
159
- fig = plotTimeSeries(Y)
160
- fig.savefig(plotFile, dpi=200, bbox_inches="tight")
161
-
162
- if verbose:
163
- print("Output plot written to:", plotFile)
164
-
165
193
  after = time.time()
166
194
  print(f"Done! ({round(after - before,2)}s)")
167
195
 
168
196
 
169
- def read(filepath, resample_hz="uniform", sample_rate=None, verbose=True):
197
+ def read(
198
+ filepath, resample_hz="uniform", sample_rate=None, lowpass_hz=None, verbose=True
199
+ ):
170
200
 
171
201
  p = pathlib.Path(filepath)
172
202
  ftype = p.suffixes[0].lower()
@@ -197,7 +227,7 @@ def read(filepath, resample_hz="uniform", sample_rate=None, verbose=True):
197
227
  data, info = actipy.process(
198
228
  data,
199
229
  sample_rate,
200
- lowpass_hz=None,
230
+ lowpass_hz=lowpass_hz,
201
231
  calibrate_gravity=True,
202
232
  detect_nonwear=True,
203
233
  resample_hz=resample_hz,
@@ -218,7 +248,7 @@ def read(filepath, resample_hz="uniform", sample_rate=None, verbose=True):
218
248
 
219
249
  data, info = actipy.read_device(
220
250
  filepath,
221
- lowpass_hz=None,
251
+ lowpass_hz=lowpass_hz,
222
252
  calibrate_gravity=True,
223
253
  detect_nonwear=True,
224
254
  resample_hz=resample_hz,
@@ -240,14 +270,20 @@ def resolve_path(path):
240
270
  return dirname, filename, extension
241
271
 
242
272
 
243
- def load_model(model_path, ssl_repo_path=None, check_md5=True, force_download=False, verbose=True):
244
- """Load trained model. Download if not exists."""
273
+ def load_classifier(
274
+ classifier_path,
275
+ model_repo_path=None,
276
+ check_md5=True,
277
+ force_download=False,
278
+ verbose=True,
279
+ ):
280
+ """Load trained classifier. Download if not exists."""
245
281
 
246
- pth = pathlib.Path(model_path)
282
+ pth = pathlib.Path(classifier_path)
247
283
 
248
284
  if force_download or not pth.exists():
249
285
 
250
- url = f"{BASE_URL}{__model_version__}.joblib.lzma"
286
+ url = f"{BASE_URL}{__classifier_version__}.joblib.lzma"
251
287
 
252
288
  if verbose:
253
289
  print(f"Downloading {url}...")
@@ -256,20 +292,19 @@ def load_model(model_path, ssl_repo_path=None, check_md5=True, force_download=Fa
256
292
  shutil.copyfileobj(f_src, f_dst)
257
293
 
258
294
  if check_md5:
259
- assert md5(pth) == __model_md5__, (
260
- "Model file is corrupted. Please run with --force-download "
295
+ assert md5(pth) == __classifier_md5__, (
296
+ "Classifier file is corrupted. Please run with --force-download "
261
297
  "to download the model file again."
262
298
  )
263
299
 
264
- model: ActivityClassifier = joblib.load(pth)
300
+ classifier: ActivityClassifier = joblib.load(pth)
265
301
 
266
- if ssl_repo_path and pathlib.Path(ssl_repo_path).exists():
267
- if verbose:
268
- print(f"Loading ssl repository from {ssl_repo_path}.")
302
+ if model_repo_path and pathlib.Path(model_repo_path).exists() and verbose:
303
+ print(f"Loading model repository from {model_repo_path}.")
269
304
 
270
- model = model.load_ssl(ssl_repo_path)
305
+ classifier.load_model(model_repo_path)
271
306
 
272
- return model
307
+ return classifier
273
308
 
274
309
 
275
310
  def md5(fname):