scdata 1.0.0__tar.gz → 1.0.2__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 (87) hide show
  1. {scdata-1.0.0/scdata.egg-info → scdata-1.0.2}/PKG-INFO +5 -7
  2. {scdata-1.0.0 → scdata-1.0.2}/README.md +3 -5
  3. {scdata-1.0.0 → scdata-1.0.2}/requirements.txt +1 -1
  4. {scdata-1.0.0 → scdata-1.0.2}/scdata/__init__.py +1 -1
  5. {scdata-1.0.0 → scdata-1.0.2}/scdata/_config/config.py +2 -16
  6. {scdata-1.0.0 → scdata-1.0.2}/scdata/device/device.py +2 -0
  7. {scdata-1.0.0 → scdata-1.0.2}/scdata/io/device_file.py +1 -1
  8. {scdata-1.0.0 → scdata-1.0.2}/scdata/models/models.py +1 -1
  9. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/plot_tools.py +37 -16
  10. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/ts_dispersion_uplot.py +1 -1
  11. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/ts_uplot.py +1 -1
  12. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/test.py +27 -16
  13. scdata-1.0.2/scdata/test/tools/combine.py +64 -0
  14. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/units.py +1 -1
  15. {scdata-1.0.0 → scdata-1.0.2/scdata.egg-info}/PKG-INFO +5 -7
  16. {scdata-1.0.0 → scdata-1.0.2}/scdata.egg-info/requires.txt +1 -1
  17. {scdata-1.0.0 → scdata-1.0.2}/setup.py +1 -1
  18. scdata-1.0.0/scdata/test/tools/combine.py +0 -60
  19. {scdata-1.0.0 → scdata-1.0.2}/LICENSE +0 -0
  20. {scdata-1.0.0 → scdata-1.0.2}/MANIFEST.in +0 -0
  21. {scdata-1.0.0 → scdata-1.0.2}/scdata/_config/__init__.py +0 -0
  22. {scdata-1.0.0 → scdata-1.0.2}/scdata/_config/custom_logger.py +0 -0
  23. {scdata-1.0.0 → scdata-1.0.2}/scdata/device/__init__.py +0 -0
  24. {scdata-1.0.0 → scdata-1.0.2}/scdata/device/process/__init__.py +0 -0
  25. {scdata-1.0.0 → scdata-1.0.2}/scdata/device/process/alphasense.py +0 -0
  26. {scdata-1.0.0 → scdata-1.0.2}/scdata/device/process/baseline.py +0 -0
  27. {scdata-1.0.0 → scdata-1.0.2}/scdata/device/process/formulae.py +0 -0
  28. {scdata-1.0.0 → scdata-1.0.2}/scdata/device/process/geoseries.py +0 -0
  29. {scdata-1.0.0 → scdata-1.0.2}/scdata/device/process/params.py +0 -0
  30. {scdata-1.0.0 → scdata-1.0.2}/scdata/device/process/regression.py +0 -0
  31. {scdata-1.0.0 → scdata-1.0.2}/scdata/device/process/timeseries.py +0 -0
  32. {scdata-1.0.0 → scdata-1.0.2}/scdata/io/__init__.py +0 -0
  33. {scdata-1.0.0 → scdata-1.0.2}/scdata/io/device_api.py +0 -0
  34. {scdata-1.0.0 → scdata-1.0.2}/scdata/io/model.py +0 -0
  35. {scdata-1.0.0 → scdata-1.0.2}/scdata/models/__init__.py +0 -0
  36. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/__init__.py +0 -0
  37. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/checks/__init__.py +0 -0
  38. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/checks/checks.py +0 -0
  39. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/dispersion/__init__.py +0 -0
  40. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/dispersion/dispersion.py +0 -0
  41. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/export/__init__.py +0 -0
  42. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/export/templates/sc_template.html +0 -0
  43. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/export/to_file.py +0 -0
  44. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/__init__.py +0 -0
  45. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/box_plot.py +0 -0
  46. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/heatmap_iplot.py +0 -0
  47. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/heatmap_plot.py +0 -0
  48. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/maps.py +0 -0
  49. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/scatter_dispersion_grid.py +0 -0
  50. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/scatter_iplot.py +0 -0
  51. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/scatter_plot.py +0 -0
  52. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/target_diagram.py +0 -0
  53. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/ts_dendrogram.py +0 -0
  54. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/ts_dispersion_grid.py +0 -0
  55. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/ts_dispersion_plot.py +0 -0
  56. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/ts_iplot.py +0 -0
  57. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/ts_plot.py +0 -0
  58. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/plot/ts_scatter.py +0 -0
  59. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/tools/__init__.py +0 -0
  60. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/tools/history.py +0 -0
  61. {scdata-1.0.0 → scdata-1.0.2}/scdata/test/tools/prepare.py +0 -0
  62. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/__init__.py +0 -0
  63. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/cleaning.py +0 -0
  64. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/custom_logger.py +0 -0
  65. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/date.py +0 -0
  66. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/dictmerge.py +0 -0
  67. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/find.py +0 -0
  68. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/gets.py +0 -0
  69. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/interim/example.csv +0 -0
  70. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/interim/geodata.csv +0 -0
  71. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/lazy.py +0 -0
  72. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/location.py +0 -0
  73. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/report.py +0 -0
  74. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/stats.py +0 -0
  75. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/uploads/example_upload_1.json +0 -0
  76. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/uploads/example_zenodo_upload.yaml +0 -0
  77. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/uploads/report.pdf +0 -0
  78. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/url_check.py +0 -0
  79. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/zenodo.py +0 -0
  80. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/zenodo_templates/README.md +0 -0
  81. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/zenodo_templates/template_zenodo_dataset.json +0 -0
  82. {scdata-1.0.0 → scdata-1.0.2}/scdata/tools/zenodo_templates/template_zenodo_publication.json +0 -0
  83. {scdata-1.0.0 → scdata-1.0.2}/scdata.egg-info/SOURCES.txt +0 -0
  84. {scdata-1.0.0 → scdata-1.0.2}/scdata.egg-info/dependency_links.txt +0 -0
  85. {scdata-1.0.0 → scdata-1.0.2}/scdata.egg-info/not-zip-safe +0 -0
  86. {scdata-1.0.0 → scdata-1.0.2}/scdata.egg-info/top_level.txt +0 -0
  87. {scdata-1.0.0 → scdata-1.0.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scdata
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Analysis of sensors and time series data
5
5
  Home-page: https://github.com/fablabbcn/smartcitizen-data
6
6
  Author: oscgonfer
@@ -29,7 +29,7 @@ Requires-Dist: numpy~=1.25.2
29
29
  Requires-Dist: pandas~=2.2.2
30
30
  Requires-Dist: pydantic
31
31
  Requires-Dist: pytest
32
- Requires-Dist: PyYAML==5.3.1
32
+ Requires-Dist: PyYAML~=6.0.1
33
33
  Requires-Dist: requests
34
34
  Requires-Dist: scipy
35
35
  Requires-Dist: scikit-learn
@@ -46,21 +46,19 @@ Smart Citizen Data
46
46
  [![DOI](https://zenodo.org/badge/97752018.svg)](https://zenodo.org/badge/latestdoi/97752018)
47
47
  [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/fablabbcn/smartcitizen-data-framework/master?filepath=%2Fexamples%2Fnotebooks)
48
48
  [![PyPI version](https://badge.fury.io/py/scdata.svg)](https://badge.fury.io/py/scdata)
49
- [![Python application](https://github.com/fablabbcn/smartcitizen-data/actions/workflows/python-app.yml/badge.svg)](https://github.com/fablabbcn/smartcitizen-data/actions/workflows/python-app.yml)
49
+ [![Python application](https://github.com/fablabbcn/smartcitizen-data/actions/workflows/python-multiple-versions.yml/badge.svg)](https://github.com/fablabbcn/smartcitizen-data/actions/workflows/python-multiple-versions.yml)
50
50
 
51
51
  Welcome to **SmartCitizen Data**. This is a data analysis framework for working with sensor data in different ways:
52
52
 
53
53
  - Interacting with several sensors APIs
54
54
  - Clean data, export and calculate metrics
55
55
  - Model sensor data and calibrate sensors
56
- - Generate data visualisations - matplotlib, [plotly](https://plotly.com/) or [uplot](https://leeoniya.github.io/uPlot)
56
+ - Generate data visualisations - matplotlib, ~[plotly](https://plotly.com/)~ or [uplot](https://leeoniya.github.io/uPlot)
57
57
  - Generate analysis reports in html or pdf and upload them to [Zenodo](http://zenodo.org)
58
58
 
59
- A full documentation of the framework is detailed in [the Smart Citizen Docs](https://docs.smartcitizen.me/Data/Data%20Analysis/).
60
-
61
59
  ## Installation
62
60
 
63
- You can check it out in the [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/fablabbcn/smartcitizen-data-framework/master?filepath=%2Fexamples%2Fnotebooks) before installing if you want. Works with `Python 3.*` (tested until `python 3.9.5`).
61
+ You can check it out in the [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/fablabbcn/smartcitizen-data-framework/master?filepath=%2Fexamples%2Fnotebooks) before installing if you want. Works with `Python 3.*` (tested between 3.9 and 3.11).
64
62
 
65
63
  You can just run:
66
64
 
@@ -4,21 +4,19 @@ Smart Citizen Data
4
4
  [![DOI](https://zenodo.org/badge/97752018.svg)](https://zenodo.org/badge/latestdoi/97752018)
5
5
  [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/fablabbcn/smartcitizen-data-framework/master?filepath=%2Fexamples%2Fnotebooks)
6
6
  [![PyPI version](https://badge.fury.io/py/scdata.svg)](https://badge.fury.io/py/scdata)
7
- [![Python application](https://github.com/fablabbcn/smartcitizen-data/actions/workflows/python-app.yml/badge.svg)](https://github.com/fablabbcn/smartcitizen-data/actions/workflows/python-app.yml)
7
+ [![Python application](https://github.com/fablabbcn/smartcitizen-data/actions/workflows/python-multiple-versions.yml/badge.svg)](https://github.com/fablabbcn/smartcitizen-data/actions/workflows/python-multiple-versions.yml)
8
8
 
9
9
  Welcome to **SmartCitizen Data**. This is a data analysis framework for working with sensor data in different ways:
10
10
 
11
11
  - Interacting with several sensors APIs
12
12
  - Clean data, export and calculate metrics
13
13
  - Model sensor data and calibrate sensors
14
- - Generate data visualisations - matplotlib, [plotly](https://plotly.com/) or [uplot](https://leeoniya.github.io/uPlot)
14
+ - Generate data visualisations - matplotlib, ~[plotly](https://plotly.com/)~ or [uplot](https://leeoniya.github.io/uPlot)
15
15
  - Generate analysis reports in html or pdf and upload them to [Zenodo](http://zenodo.org)
16
16
 
17
- A full documentation of the framework is detailed in [the Smart Citizen Docs](https://docs.smartcitizen.me/Data/Data%20Analysis/).
18
-
19
17
  ## Installation
20
18
 
21
- You can check it out in the [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/fablabbcn/smartcitizen-data-framework/master?filepath=%2Fexamples%2Fnotebooks) before installing if you want. Works with `Python 3.*` (tested until `python 3.9.5`).
19
+ You can check it out in the [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/fablabbcn/smartcitizen-data-framework/master?filepath=%2Fexamples%2Fnotebooks) before installing if you want. Works with `Python 3.*` (tested between 3.9 and 3.11).
22
20
 
23
21
  You can just run:
24
22
 
@@ -16,7 +16,7 @@ pandas~=2.2.2
16
16
  pydantic
17
17
  pytest
18
18
  # TODO To be updated?
19
- PyYAML==5.3.1
19
+ PyYAML~=6.0.1
20
20
  requests
21
21
  scipy
22
22
  scikit-learn
@@ -3,4 +3,4 @@ from .device import Device
3
3
  from .test import Test
4
4
  from .models import Source, TestOptions, DeviceOptions, APIParams, FileParams, CSVParams
5
5
 
6
- __version__ = '1.0.0'
6
+ __version__ = '1.0.2'
@@ -38,9 +38,6 @@ class Config(object):
38
38
  if 'IPython' in sys.modules: _ipython_avail = True
39
39
  else: _ipython_avail = False
40
40
 
41
- # Returns when iterables cannot be fully processed
42
- _strict = False
43
-
44
41
  # Timeout for http requests
45
42
  _timeout = 3
46
43
  _max_http_retries = 2
@@ -51,20 +48,9 @@ class Config(object):
51
48
  ### ---------------------------------------
52
49
  ### -----------------DATA------------------
53
50
  ### ---------------------------------------
54
-
55
51
  data = {
56
- # Whether or not to reload metadata from git repo
57
- 'reload_metadata': True,
58
- # Whether or not to load or store cached data (saves time when requesting a lot of data)
59
- 'load_cached_api': True,
60
- 'store_cached_api': True,
61
- # If reloading data from the API, how much gap between the saved data and the
62
- # latest reading in the API should be ignore
63
- 'cached_data_margin': 1,
64
- # clean_na
65
- 'clean_na': None,
66
- # Ignore additional channels from API or CSV that are not in the blueprint.json
67
- 'strict_load': False
52
+ 'cached_data_margin': '10Min', # Data Margin in minutes for consecutive requests
53
+ 'reload_metadata': True # Reload metadata
68
54
  }
69
55
 
70
56
  # Maximum amount of points to load when postprocessing data
@@ -300,6 +300,7 @@ class Device(BaseModel):
300
300
  else:
301
301
  raise NotImplementedError(f'Cache needs to be a .csv file. Got {cache}.')
302
302
 
303
+ # Make request with a logical min_date
303
304
  if not cached_data.empty:
304
305
  # Update min_date
305
306
  min_date=cached_data.index[-1].tz_convert('UTC')+Timedelta(frequency)
@@ -327,6 +328,7 @@ class Device(BaseModel):
327
328
  # In principle this links both dataframes as they are unmutable
328
329
  self.data = self.handler.data
329
330
  # Wrap it all up
331
+ # TODO Avoid doing this if not needed?
330
332
  self.loaded = self.__load_wrapup__(max_amount, convert_units=convert_units, convert_names=convert_names, cached_data=cached_data)
331
333
 
332
334
  self.processed = False
@@ -128,7 +128,7 @@ def read_csv_file(path, timezone, frequency=None, clean_na=None, index_name='',
128
128
 
129
129
  # Read pandas dataframe
130
130
 
131
- df = read_csv(path, verbose=False, skiprows=skiprows, sep=sep,
131
+ df = read_csv(path, skiprows=skiprows, sep=sep,
132
132
  encoding=encoding, encoding_errors='ignore')
133
133
 
134
134
  flag_found = False
@@ -3,7 +3,7 @@ from typing import Optional, List
3
3
  from datetime import datetime
4
4
 
5
5
  class TestOptions(BaseModel):
6
- cache: Optional[bool] = False
6
+ cache: Optional[bool] = True
7
7
 
8
8
  class Metric(BaseModel):
9
9
  id: Optional[int] = None
@@ -93,14 +93,15 @@ def prepare_data(test, traces, options):
93
93
  logger.warning(f'The trace {traces[trace]} was not placed in any subplot. Assuming subplot #1')
94
94
  traces[trace]['subplot'] = 1
95
95
 
96
+
96
97
  ndevs = traces[trace]['devices']
97
98
  nchans = traces[trace]['channel']
98
99
 
99
100
  # Make them lists always
100
101
  if ndevs == 'all': devices = [device.id for device in test.devices]
102
+ ## TODO Make it regex compatible!
101
103
  elif type(ndevs) == str or type(ndevs) == int: devices = [ndevs]
102
104
  else: devices = ndevs
103
- print (devices)
104
105
 
105
106
  for ndev in devices:
106
107
 
@@ -158,8 +159,23 @@ def prepare_data(test, traces, options):
158
159
  # Remove column for filtering from dfdev
159
160
  dfdev.drop(columns=[col_name], inplace = True)
160
161
 
162
+ # Resample it
163
+ if options['frequency'] is not None:
164
+ logger.info(f"Resampling at {options['frequency']}")
165
+
166
+ if 'resample' in options:
167
+
168
+ if options['resample'] == 'max': dfdev = dfdev.resample(options['frequency']).max()
169
+ if options['resample'] == 'min': dfdev = dfdev.resample(options['frequency']).min()
170
+ if options['resample'] == 'mean': dfdev = dfdev.resample(options['frequency']).mean()
171
+
172
+ else:
173
+ dfdev = dfdev.resample(options['frequency']).mean()
174
+
161
175
  # Combine it in the df
162
176
  df = df.combine_first(dfdev)
177
+
178
+ ## TODO Is this working?
163
179
  # Add average or other extras
164
180
  # TODO Check this to simplify
165
181
  # https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.core.resample.Resampler.aggregate.html
@@ -168,15 +184,19 @@ def prepare_data(test, traces, options):
168
184
 
169
185
  nextras = list()
170
186
  for device in traces[trace]['devices']:
171
- for channel in traces[trace]['channel']:
172
- nextras.append(f'{channel}_{ndev}')
187
+ if type(traces[trace]['channel']) == 'list':
188
+ for channel in traces[trace]['channel']:
189
+ nextras.append(f'{channel}_{device}')
190
+ else:
191
+ nextras.append(f"{traces[trace]['channel']}_{device}")
173
192
 
174
193
  if extra == 'bands':
175
- ubn = channel + f"-{trace}-{'UPPER-BAND'}"
176
- lbn = channel + f"-{trace}-{'LOWER-BAND'}"
194
+ ubn = channel + f"-{trace}-UPPER-BAND"
195
+ lbn = channel + f"-{trace}-LOWER-BAND"
177
196
 
178
- df[ubn] = df.loc[:, nextras].mean(axis = 1) + 2*df.loc[:, nextras].std(axis = 1)
179
- df[lbn] = df.loc[:, nextras].mean(axis = 1) - 2*df.loc[:, nextras].std(axis = 1)
197
+ logger.info('Using 3sig bands')
198
+ df[ubn] = df.loc[:, nextras].mean(axis = 1) + 3*df.loc[:, nextras].std(axis = 1)
199
+ df[lbn] = df.loc[:, nextras].mean(axis = 1) - 3*df.loc[:, nextras].std(axis = 1)
180
200
 
181
201
  subplots[traces[trace]['subplot']-1].append(ubn)
182
202
  subplots[traces[trace]['subplot']-1].append(lbn)
@@ -208,18 +228,19 @@ def prepare_data(test, traces, options):
208
228
  if df.empty:
209
229
  logger.error('Empty dataframe for plot')
210
230
  return None, None
211
- # Resample it
212
- if options['frequency'] is not None:
213
- logger.info(f"Resampling at {options['frequency']}")
214
231
 
215
- if 'resample' in options:
232
+ # # Resample it
233
+ # if options['frequency'] is not None:
234
+ # logger.info(f"Resampling at {options['frequency']}")
216
235
 
217
- if options['resample'] == 'max': df = df.resample(options['frequency']).max()
218
- if options['resample'] == 'min': df = df.resample(options['frequency']).min()
219
- if options['resample'] == 'mean': df = df.resample(options['frequency']).mean()
236
+ # if 'resample' in options:
220
237
 
221
- else:
222
- df = df.resample(options['frequency']).mean()
238
+ # if options['resample'] == 'max': df = df.resample(options['frequency']).max()
239
+ # if options['resample'] == 'min': df = df.resample(options['frequency']).min()
240
+ # if options['resample'] == 'mean': df = df.resample(options['frequency']).mean()
241
+
242
+ # else:
243
+ # df = df.resample(options['frequency']).mean()
223
244
 
224
245
  # Clean na
225
246
  if options['clean_na'] is not None:
@@ -149,7 +149,7 @@ def ts_dispersion_uplot(self, **kwargs):
149
149
 
150
150
  if formatting['join_sbplot']: n_subplots = 1
151
151
  else: n_subplots = 2
152
- udf.index = udf.index.astype(int)/10**9
152
+ udf.index = udf.index.astype('int64')/10**9
153
153
 
154
154
  # Compose subplots lists
155
155
  for device in self.devices:
@@ -112,7 +112,7 @@ def ts_uplot(self, **kwargs):
112
112
 
113
113
  # Get data in uplot expected format
114
114
  udf = df.copy()
115
- udf.index = udf.index.astype(int)/10**9
115
+ udf.index = udf.index.astype('int64')/10**9
116
116
 
117
117
  for isbplt in range(n_subplots):
118
118
 
@@ -67,7 +67,6 @@ class Test(BaseModel):
67
67
 
68
68
  self.devices = TypeAdapter(List[Device]).validate_python(tj['devices'])
69
69
  self.options = TypeAdapter(TestOptions).validate_python(tj['options'])
70
- print (tj['meta'])
71
70
  self.type = tj['meta']['type']
72
71
  if self.name != tj['meta']['name']:
73
72
  raise ValueError('Name not matching')
@@ -334,20 +333,20 @@ class Test(BaseModel):
334
333
 
335
334
  return fname
336
335
 
337
- def cache(self):
338
- logger.info(f'Caching files...')
339
- for device in self.devices:
340
- logger.info(f'Caching files for {device.id}...')
336
+ # def cache(self):
337
+ # logger.info(f'Caching files...')
338
+ # for device in self.devices:
339
+ # logger.info(f'Caching files for {device.id}...')
341
340
 
342
- cached_file_path = join(self.path, 'cached')
343
- if not exists(cached_file_path):
344
- logger.info('Creating path for exporting cached data')
345
- makedirs(cached_file_path)
341
+ # cached_file_path = join(self.path, 'cached')
342
+ # if not exists(cached_file_path):
343
+ # logger.info('Creating path for exporting cached data')
344
+ # makedirs(cached_file_path)
346
345
 
347
- if device.export(cached_file_path, forced_overwrite = True, file_format = 'csv'):
348
- logger.info(f'Device {device.id} cached')
346
+ # if device.export(cached_file_path, forced_overwrite = True, file_format = 'csv'):
347
+ # logger.info(f'Device {device.id} cached')
349
348
 
350
- return all([exists(join(self.path, 'cached', f'{d.id}.csv')) for d in self.devices])
349
+ # return all([exists(join(self.path, 'cached', f'{d.id}.csv')) for d in self.devices])
351
350
 
352
351
  async def load(self):
353
352
  '''
@@ -359,17 +358,29 @@ class Test(BaseModel):
359
358
  '''
360
359
  logger.info('Loading test...')
361
360
 
361
+ if self.options.cache:
362
+ cache_dir = join(self.path, 'cached')
363
+ if not exists(cache_dir):
364
+ logger.info('Creating path for exporting cached data...')
365
+ makedirs(cache_dir)
366
+ logger.info(f'Cache will be available in: {cache_dir}')
367
+
362
368
  for device in self.devices:
363
369
  # Check for cached data
364
- cached_file_path = ''
370
+ device_cache_path = ''
365
371
  if self.options.cache:
366
372
  tentative_path = join(self.path, 'cached', f'{device.id}.csv')
367
- if exists(tentative_path): cached_file_path = tentative_path
373
+ if exists(tentative_path):
374
+ device_cache_path = tentative_path
375
+
368
376
  # Load device (no need to go async, it's fast enough)
369
- await device.load(cache=cached_file_path)
377
+ await device.load(cache=device_cache_path)
378
+
379
+ if self.options.cache:
380
+ if device.export(cache_dir, forced_overwrite = True, file_format = 'csv'):
381
+ logger.info(f'Device {device.id} cached')
370
382
 
371
383
  logger.info('Test load done')
372
- if self.options.cache: self.cache()
373
384
 
374
385
  self.loaded = all([d.loaded for d in self.devices])
375
386
  return self.loaded
@@ -0,0 +1,64 @@
1
+ from pandas import DataFrame
2
+ from scdata.tools.custom_logger import logger
3
+ from scdata.device import Device
4
+
5
+ def combine(self, devices = None, channels = None, resample = True, frequency = '1Min'):
6
+ """
7
+ Combines devices from a test into a new dataframe, following the
8
+ naming as follows: DEVICE-NAME_READING-NAME
9
+ Parameters
10
+ ----------
11
+ devices: list or None
12
+ None
13
+ If None, includes all the devices in self.devices
14
+ channels: list or None
15
+ None
16
+ If None, includes all the readings in self.readings
17
+ Returns
18
+ -------
19
+ Dataframe if successful or False otherwise
20
+ """
21
+
22
+ dfc = DataFrame()
23
+ if not self.loaded:
24
+ logger.error('Cannot combine data if test is not loaded. Maybe test.load() first?')
25
+
26
+ if devices is None:
27
+ dl = [device.id for device in self.devices]
28
+ else:
29
+ # Only requested AND available
30
+ dl = list(set(devices).intersection([device.id for device in self.devices]))
31
+ if len(dl) != len(devices):
32
+ logger.warning('Requested devices are not all present in devices')
33
+ logger.info(f'Discarding {set(devices).difference([device.id for device in self.devices])}')
34
+
35
+ for device in dl:
36
+ new_names = list()
37
+
38
+ if channels is None:
39
+ channel_list = list(self.get_device(device).data.columns)
40
+ else:
41
+ # Only pick the ones that are actually present
42
+ channel_list = list(set(channels).intersection(list(self.get_device(device).data.columns)))
43
+
44
+ if any([channel not in channel_list for channel in channels]):
45
+ logger.warning(f'Requested channels are not all present in readings for device {device}')
46
+ logger.warning(f'Discarding {list(set(channels).difference(list(self.get_device(device).data.columns)))}')
47
+
48
+ rename = dict()
49
+
50
+ for channel in channel_list:
51
+ rename[channel] = f'{channel}_{self.get_device(device).id}'
52
+
53
+ df = self.get_device(device).data[channel_list].copy()
54
+ df.rename(columns = rename, inplace = True)
55
+ if resample:
56
+ df = df.resample(frequency).mean()
57
+ dfc = dfc.combine_first(df)
58
+
59
+ if dfc.empty:
60
+ logger.error('Error ocurred while combining data. Review data')
61
+ return False
62
+ else:
63
+ logger.info('Data combined successfully')
64
+ return dfc
@@ -92,7 +92,7 @@ def get_units_convf(sensor, from_units):
92
92
  else: molecular_weight = 1
93
93
 
94
94
  # Check if channel is in look-up table
95
- if channel_lut[channel] != from_units and from_units != "":
95
+ if channel_lut[channel] != from_units and from_units != "" and from_units is not None:
96
96
  logger.info(f"Converting units for {sensor}. From {from_units} to {channel_lut[channel]}")
97
97
  for unit in unit_convertion_lut:
98
98
  # Get units
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: scdata
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: Analysis of sensors and time series data
5
5
  Home-page: https://github.com/fablabbcn/smartcitizen-data
6
6
  Author: oscgonfer
@@ -29,7 +29,7 @@ Requires-Dist: numpy~=1.25.2
29
29
  Requires-Dist: pandas~=2.2.2
30
30
  Requires-Dist: pydantic
31
31
  Requires-Dist: pytest
32
- Requires-Dist: PyYAML==5.3.1
32
+ Requires-Dist: PyYAML~=6.0.1
33
33
  Requires-Dist: requests
34
34
  Requires-Dist: scipy
35
35
  Requires-Dist: scikit-learn
@@ -46,21 +46,19 @@ Smart Citizen Data
46
46
  [![DOI](https://zenodo.org/badge/97752018.svg)](https://zenodo.org/badge/latestdoi/97752018)
47
47
  [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/fablabbcn/smartcitizen-data-framework/master?filepath=%2Fexamples%2Fnotebooks)
48
48
  [![PyPI version](https://badge.fury.io/py/scdata.svg)](https://badge.fury.io/py/scdata)
49
- [![Python application](https://github.com/fablabbcn/smartcitizen-data/actions/workflows/python-app.yml/badge.svg)](https://github.com/fablabbcn/smartcitizen-data/actions/workflows/python-app.yml)
49
+ [![Python application](https://github.com/fablabbcn/smartcitizen-data/actions/workflows/python-multiple-versions.yml/badge.svg)](https://github.com/fablabbcn/smartcitizen-data/actions/workflows/python-multiple-versions.yml)
50
50
 
51
51
  Welcome to **SmartCitizen Data**. This is a data analysis framework for working with sensor data in different ways:
52
52
 
53
53
  - Interacting with several sensors APIs
54
54
  - Clean data, export and calculate metrics
55
55
  - Model sensor data and calibrate sensors
56
- - Generate data visualisations - matplotlib, [plotly](https://plotly.com/) or [uplot](https://leeoniya.github.io/uPlot)
56
+ - Generate data visualisations - matplotlib, ~[plotly](https://plotly.com/)~ or [uplot](https://leeoniya.github.io/uPlot)
57
57
  - Generate analysis reports in html or pdf and upload them to [Zenodo](http://zenodo.org)
58
58
 
59
- A full documentation of the framework is detailed in [the Smart Citizen Docs](https://docs.smartcitizen.me/Data/Data%20Analysis/).
60
-
61
59
  ## Installation
62
60
 
63
- You can check it out in the [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/fablabbcn/smartcitizen-data-framework/master?filepath=%2Fexamples%2Fnotebooks) before installing if you want. Works with `Python 3.*` (tested until `python 3.9.5`).
61
+ You can check it out in the [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/fablabbcn/smartcitizen-data-framework/master?filepath=%2Fexamples%2Fnotebooks) before installing if you want. Works with `Python 3.*` (tested between 3.9 and 3.11).
64
62
 
65
63
  You can just run:
66
64
 
@@ -9,7 +9,7 @@ numpy~=1.25.2
9
9
  pandas~=2.2.2
10
10
  pydantic
11
11
  pytest
12
- PyYAML==5.3.1
12
+ PyYAML~=6.0.1
13
13
  requests
14
14
  scipy
15
15
  scikit-learn
@@ -19,7 +19,7 @@ REQUIREMENTS = [i.strip() for i in open("requirements.txt").readlines()]
19
19
 
20
20
  setup(
21
21
  name='scdata',
22
- version='1.0.0',
22
+ version='1.0.2',
23
23
  description='Analysis of sensors and time series data',
24
24
  author='oscgonfer',
25
25
  license='GNU-GPL3.0',
@@ -1,60 +0,0 @@
1
- from pandas import DataFrame
2
- from scdata.tools.custom_logger import logger
3
- from scdata.device import Device
4
-
5
- def combine(self, devices = None, readings = None):
6
- """
7
- Combines devices from a test into a new dataframe, following the
8
- naming as follows: DEVICE-NAME_READING-NAME
9
- Parameters
10
- ----------
11
- devices: list or None
12
- None
13
- If None, includes all the devices in self.devices
14
- readings: list or None
15
- None
16
- If None, includes all the readings in self.readings
17
- Returns
18
- -------
19
- Dataframe if successful or False otherwise
20
- """
21
-
22
- dfc = DataFrame()
23
-
24
- if devices is None:
25
- dl = list(self.devices.keys())
26
- else:
27
- # Only pick the ones that are actually present
28
- dl = list(set(devices).intersection(list(self.devices.keys())))
29
- if len(dl) != len(devices):
30
- logger.warning('Requested devices are not all present in devices')
31
- logger.info(f'Discarding {set(devices).difference(list(self.devices.keys()))}')
32
-
33
- for device in dl:
34
- new_names = list()
35
-
36
- if readings is None:
37
- rl = list(self.devices[device].readings.columns)
38
- else:
39
- # Only pick the ones that are actually present
40
- rl = list(set(readings).intersection(list(self.devices[device].readings.columns)))
41
-
42
- if any([reading not in rl for reading in readings]):
43
- logger.warning(f'Requested readings are not all present in readings for device {device}')
44
- logger.warning(f'Discarding {list(set(readings).difference(list(self.devices[device].readings.columns)))}')
45
-
46
- rename = dict()
47
-
48
- for reading in rl:
49
- rename[reading] = reading + '_' + self.devices[device].id
50
-
51
- df = self.devices[device].readings[rl].copy()
52
- df.rename(columns = rename, inplace = True)
53
- dfc = dfc.combine_first(df)
54
-
55
- if dfc.empty:
56
- logger.error('Error ocurred while combining data. Review data')
57
- return False
58
- else:
59
- logger.info('Data combined successfully')
60
- return dfc
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes