apmtools 0.0.1__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.
- apmtools-0.0.1/LICENSE +21 -0
- apmtools-0.0.1/PKG-INFO +15 -0
- apmtools-0.0.1/README.md +2 -0
- apmtools-0.0.1/pyproject.toml +21 -0
- apmtools-0.0.1/src/apmtools/__init__.py +0 -0
- apmtools-0.0.1/src/apmtools/classes.py +274 -0
- apmtools-0.0.1/src/apmtools/functions.py +61 -0
apmtools-0.0.1/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 federlorenz
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
apmtools-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
|
+
Name: apmtools
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: A collection of tools for processing air pollution monitoring data
|
|
5
|
+
Project-URL: Homepage, https://github.com/federlorenz/apmtools
|
|
6
|
+
Author-email: Federico Lorenzetti <lorenzetti.federico@gmail.com>
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
9
|
+
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# apmtools
|
|
15
|
+
A collection of tools for processing air pollution monitoring data
|
apmtools-0.0.1/README.md
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "apmtools"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name="Federico Lorenzetti", email="lorenzetti.federico@gmail.com" },
|
|
10
|
+
]
|
|
11
|
+
description = "A collection of tools for processing air pollution monitoring data"
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://github.com/federlorenz/apmtools"
|
|
File without changes
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DictionaryPlus(dict):
|
|
6
|
+
def __init__(self, *args, **kwargs):
|
|
7
|
+
dict.__init__(self, *args, **kwargs)
|
|
8
|
+
|
|
9
|
+
@property
|
|
10
|
+
def _constructor(self):
|
|
11
|
+
return DictionaryPlus
|
|
12
|
+
|
|
13
|
+
def show(self, number=0):
|
|
14
|
+
"""
|
|
15
|
+
return an element of a dictionary
|
|
16
|
+
If number is not specified, returns the values associated with the first key
|
|
17
|
+
"""
|
|
18
|
+
try:
|
|
19
|
+
return (self[list(self.keys())[number]])
|
|
20
|
+
except:
|
|
21
|
+
print("something's wrong")
|
|
22
|
+
|
|
23
|
+
def subset(self, filter_dict):
|
|
24
|
+
"""
|
|
25
|
+
Return a subset of a dictionary, specified in filter_dict (itself a dictionary)
|
|
26
|
+
filter_dict is {attrib:["attrib_value_x","attrib_value_y",..]}, where
|
|
27
|
+
attrib is an attribute of the elements of dictionary, and attrib_value is a list
|
|
28
|
+
of the values of such attrib that the elements of returned dictionary can have
|
|
29
|
+
"""
|
|
30
|
+
if type(filter_dict) != type(dict()):
|
|
31
|
+
print("subset function error: type filter_dict should be dict")
|
|
32
|
+
return
|
|
33
|
+
return_dict = self
|
|
34
|
+
for i, j in filter_dict.items():
|
|
35
|
+
a = {}
|
|
36
|
+
for key, value in return_dict.items():
|
|
37
|
+
if hasattr(value, 'metadata') & (type(value.metadata) == type({})) & (i in value.metadata.keys()):
|
|
38
|
+
try:
|
|
39
|
+
if value.__getattr__('metadata')[i] in j:
|
|
40
|
+
a[key] = value
|
|
41
|
+
except:
|
|
42
|
+
pass
|
|
43
|
+
else:
|
|
44
|
+
try:
|
|
45
|
+
if value.__getattr__(i) in j:
|
|
46
|
+
a[key] = value
|
|
47
|
+
except:
|
|
48
|
+
pass
|
|
49
|
+
|
|
50
|
+
return DictionaryPlus(return_dict)
|
|
51
|
+
|
|
52
|
+
def set_attrib(self, attribute):
|
|
53
|
+
"""
|
|
54
|
+
returns the set of attribute values for dictionary
|
|
55
|
+
"""
|
|
56
|
+
return_set = set()
|
|
57
|
+
for i in self.values():
|
|
58
|
+
if hasattr(i, 'metadata') & (type(i.metadata) == type({})) & (attribute in i.metadata.keys()):
|
|
59
|
+
try:
|
|
60
|
+
return_set.add(i.__getattr__('metadata')[attribute])
|
|
61
|
+
except:
|
|
62
|
+
pass
|
|
63
|
+
else:
|
|
64
|
+
try:
|
|
65
|
+
return_set.add(i.__getattr__(attribute))
|
|
66
|
+
except:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
return return_set
|
|
70
|
+
|
|
71
|
+
class Apm(pd.DataFrame):
|
|
72
|
+
|
|
73
|
+
def __init__(self, *args, **kwargs):
|
|
74
|
+
pd.DataFrame.__init__(self, *args, **kwargs)
|
|
75
|
+
self.meta = {}
|
|
76
|
+
_metadata = ['meta']
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def _constructor(self):
|
|
80
|
+
return Apm
|
|
81
|
+
|
|
82
|
+
@property
|
|
83
|
+
def _constructor_sliced(self):
|
|
84
|
+
return ApmSeries
|
|
85
|
+
|
|
86
|
+
@property
|
|
87
|
+
def end(self):
|
|
88
|
+
if len(self) == 0:
|
|
89
|
+
return np.nan
|
|
90
|
+
else:
|
|
91
|
+
return self.index[-1]
|
|
92
|
+
|
|
93
|
+
@property
|
|
94
|
+
def start(self):
|
|
95
|
+
if len(self) == 0:
|
|
96
|
+
return np.nan
|
|
97
|
+
else:
|
|
98
|
+
return self.index[0]
|
|
99
|
+
|
|
100
|
+
@property
|
|
101
|
+
def length(self):
|
|
102
|
+
if len(self) == 0:
|
|
103
|
+
return np.nan
|
|
104
|
+
else:
|
|
105
|
+
return len(self)*(self.index[1]-self.index[0])
|
|
106
|
+
|
|
107
|
+
def date_time_filter(
|
|
108
|
+
self,
|
|
109
|
+
time_start=None,
|
|
110
|
+
time_end=None,
|
|
111
|
+
date_start=None,
|
|
112
|
+
date_end=None,
|
|
113
|
+
day=None):
|
|
114
|
+
"""Filters a file by time or date\n
|
|
115
|
+
Input time as dt.time(hrs,min), and date as dt.date(year,month,day),\n
|
|
116
|
+
and day as [1,2] list of days, with 1 Monday and 7 Sunday,\n
|
|
117
|
+
if selecting a specific date interval that includes time, just specify\n
|
|
118
|
+
that as dt.datetime interval under date_start and date_end"""
|
|
119
|
+
if date_start is not None:
|
|
120
|
+
self = self.loc[self.index >= date_start]
|
|
121
|
+
if date_end is not None:
|
|
122
|
+
self = self.loc[self.index <= date_end]
|
|
123
|
+
if (time_start is not None) & (time_end is not None):
|
|
124
|
+
if time_start > time_end:
|
|
125
|
+
self = self.loc[(self.index.time >= time_start)
|
|
126
|
+
| (self.index.time < time_end)]
|
|
127
|
+
else:
|
|
128
|
+
self = self.loc[(self.index.time >= time_start)
|
|
129
|
+
& (self.index.time < time_end)]
|
|
130
|
+
if (time_start is not None) & (time_end is None):
|
|
131
|
+
self = self.loc[self.index.time >= time_start]
|
|
132
|
+
if (time_start is None) & (time_end is not None):
|
|
133
|
+
self = self.loc[self.index.time <= time_end]
|
|
134
|
+
|
|
135
|
+
if day is not None:
|
|
136
|
+
self = self.loc[[a in day for a in [self.index[i].date().isoweekday()
|
|
137
|
+
for i in range(len(self.index))]]]
|
|
138
|
+
|
|
139
|
+
return self
|
|
140
|
+
|
|
141
|
+
class ApmSeries(pd.Series):
|
|
142
|
+
def __init__(self, *args, **kwargs):
|
|
143
|
+
pd.Series.__init__(self, *args, **kwargs)
|
|
144
|
+
_metadata = ['meta']
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def _constructor(self):
|
|
148
|
+
return ApmSeries
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def end(self):
|
|
152
|
+
if len(self) == 0:
|
|
153
|
+
return np.nan
|
|
154
|
+
else:
|
|
155
|
+
return self.index[-1]
|
|
156
|
+
|
|
157
|
+
@property
|
|
158
|
+
def start(self):
|
|
159
|
+
if len(self) == 0:
|
|
160
|
+
return np.nan
|
|
161
|
+
else:
|
|
162
|
+
return self.index[0]
|
|
163
|
+
|
|
164
|
+
@property
|
|
165
|
+
def length(self):
|
|
166
|
+
if len(self) == 0:
|
|
167
|
+
return np.nan
|
|
168
|
+
else:
|
|
169
|
+
return len(self)*(self.index[1]-self.index[0])
|
|
170
|
+
|
|
171
|
+
def date_time_filter(
|
|
172
|
+
self,
|
|
173
|
+
time_start=None,
|
|
174
|
+
time_end=None,
|
|
175
|
+
date_start=None,
|
|
176
|
+
date_end=None,
|
|
177
|
+
day=None):
|
|
178
|
+
"""Filters a file by time or date\n
|
|
179
|
+
Input time as dt.time(hrs,min), and date as dt.date(year,month,day),\n
|
|
180
|
+
and day as [1,2] list of days, with 1 Monday and 7 Sunday,\n
|
|
181
|
+
if selecting a specific date interval that includes time, just specify\n
|
|
182
|
+
that as dt.datetime interval under date_start and date_end"""
|
|
183
|
+
if date_start is not None:
|
|
184
|
+
self = self.loc[self.index >= date_start]
|
|
185
|
+
if date_end is not None:
|
|
186
|
+
self = self.loc[self.index <= date_end]
|
|
187
|
+
if (time_start is not None) & (time_end is not None):
|
|
188
|
+
if time_start > time_end:
|
|
189
|
+
self = self.loc[(self.index.time >= time_start)
|
|
190
|
+
| (self.index.time < time_end)]
|
|
191
|
+
else:
|
|
192
|
+
self = self.loc[(self.index.time >= time_start)
|
|
193
|
+
& (self.index.time < time_end)]
|
|
194
|
+
if (time_start is not None) & (time_end is None):
|
|
195
|
+
self = self.loc[self.index.time >= time_start]
|
|
196
|
+
if (time_start is None) & (time_end is not None):
|
|
197
|
+
self = self.loc[self.index.time <= time_end]
|
|
198
|
+
|
|
199
|
+
if day is not None:
|
|
200
|
+
self = self.loc[[a in day for a in [self.index[i].date().isoweekday()
|
|
201
|
+
for i in range(len(self.index))]]]
|
|
202
|
+
|
|
203
|
+
return self
|
|
204
|
+
|
|
205
|
+
class Sum(Apm):
|
|
206
|
+
def __init__(self, *args, **kwargs):
|
|
207
|
+
Apm.__init__(self, *args, **kwargs)
|
|
208
|
+
self.metadata = {}
|
|
209
|
+
_metadata = ['meta']
|
|
210
|
+
|
|
211
|
+
@property
|
|
212
|
+
def _constructor(self):
|
|
213
|
+
return Sum
|
|
214
|
+
|
|
215
|
+
@property
|
|
216
|
+
def _constructor_sliced(self):
|
|
217
|
+
return SumSeries
|
|
218
|
+
|
|
219
|
+
@property
|
|
220
|
+
def number_of_events(self):
|
|
221
|
+
if len(self) == 0:
|
|
222
|
+
return np.nan
|
|
223
|
+
else:
|
|
224
|
+
return len(self["cooking_counter"].value_counts())
|
|
225
|
+
|
|
226
|
+
@property
|
|
227
|
+
def max_event_length(self):
|
|
228
|
+
if len(self) == 0:
|
|
229
|
+
return np.nan
|
|
230
|
+
else:
|
|
231
|
+
return (self["cooking_counter"].value_counts().max())*((self.index[1]-self.index[0]))
|
|
232
|
+
|
|
233
|
+
@property
|
|
234
|
+
def min_event_length(self):
|
|
235
|
+
if len(self) == 0:
|
|
236
|
+
return np.nan
|
|
237
|
+
else:
|
|
238
|
+
return (self["cooking_counter"].value_counts().min())*((self.index[1]-self.index[0]))
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def mean_event_length(self):
|
|
242
|
+
if len(self) == 0:
|
|
243
|
+
return np.nan
|
|
244
|
+
else:
|
|
245
|
+
return (self["cooking_counter"].value_counts().mean())*((self.index[1]-self.index[0]))
|
|
246
|
+
|
|
247
|
+
@property
|
|
248
|
+
def cooking_time_per_day(self):
|
|
249
|
+
if len(self) == 0:
|
|
250
|
+
return np.nan
|
|
251
|
+
elif len(self["cooking_counter"].value_counts()) == 0:
|
|
252
|
+
return pd.Timedelta("00:00:00")
|
|
253
|
+
else:
|
|
254
|
+
return ((self["cooking_counter"].value_counts().sum())*((self.index[1]-self.index[0])) / self.length) * \
|
|
255
|
+
pd.Timedelta("24:00:00")
|
|
256
|
+
|
|
257
|
+
@property
|
|
258
|
+
def cooking_events_per_day(self):
|
|
259
|
+
if len(self) == 0:
|
|
260
|
+
return np.nan
|
|
261
|
+
elif len(self["cooking_counter"].value_counts()) == 0:
|
|
262
|
+
return 0
|
|
263
|
+
else:
|
|
264
|
+
return self.number_of_events / \
|
|
265
|
+
(self.length.total_seconds() / (3600 * 24))
|
|
266
|
+
|
|
267
|
+
class SumSeries(ApmSeries):
|
|
268
|
+
def __init__(self, *args, **kwargs):
|
|
269
|
+
ApmSeries.__init__(self, *args, **kwargs)
|
|
270
|
+
_metadata = ['meta']
|
|
271
|
+
|
|
272
|
+
@property
|
|
273
|
+
def _constructor(self):
|
|
274
|
+
return SumSeries
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
def show(dictionary, number=0):
|
|
2
|
+
"""
|
|
3
|
+
return an element of a dictionary
|
|
4
|
+
If number is not specified, returns the values associated with the first key
|
|
5
|
+
"""
|
|
6
|
+
try:
|
|
7
|
+
return(dictionary[list(dictionary.keys())[number]])
|
|
8
|
+
except:
|
|
9
|
+
print("something's wrong")
|
|
10
|
+
|
|
11
|
+
def subset(dictionary, filter_dict):
|
|
12
|
+
"""
|
|
13
|
+
Return a subset of a dictionary, specified in filter_dict (itself a dictionary)
|
|
14
|
+
filter_dict is {attrib:["attrib_value_x","attrib_value_y",..]}, where
|
|
15
|
+
attrib is an attribute of the elements of dictionary, or a metadata attrib and attrib_value is a list
|
|
16
|
+
of the values of such attrib that the elements of returned dictionary can have
|
|
17
|
+
"""
|
|
18
|
+
if type(dictionary) != type(dict()):
|
|
19
|
+
print("subset function error: type dictionary should be dict")
|
|
20
|
+
return
|
|
21
|
+
if type(filter_dict) != type(dict()):
|
|
22
|
+
print("subset function error: type filter_dict should be dict")
|
|
23
|
+
return
|
|
24
|
+
return_dict = dictionary
|
|
25
|
+
for i, j in filter_dict.items():
|
|
26
|
+
a = {}
|
|
27
|
+
for key,value in return_dict.items():
|
|
28
|
+
if hasattr(value,'meta') & (type(value.meta) == type({})) & (i in value.meta.keys()):
|
|
29
|
+
try:
|
|
30
|
+
if value.__getattr__('meta')[i] in j:
|
|
31
|
+
a[key] = value
|
|
32
|
+
except:
|
|
33
|
+
pass
|
|
34
|
+
else:
|
|
35
|
+
try:
|
|
36
|
+
if value.__getattr__(i) in j:
|
|
37
|
+
a[key] = value
|
|
38
|
+
except:
|
|
39
|
+
pass
|
|
40
|
+
return_dict = a
|
|
41
|
+
|
|
42
|
+
return return_dict
|
|
43
|
+
|
|
44
|
+
def set_attrib(dictionary, attribute):
|
|
45
|
+
"""
|
|
46
|
+
returns the set of attribute values for dictionary
|
|
47
|
+
"""
|
|
48
|
+
return_set = set()
|
|
49
|
+
for i in dictionary.values():
|
|
50
|
+
if hasattr(i, 'meta') & (type(i.meta) == type({})) & (attribute in i.meta.keys()):
|
|
51
|
+
try:
|
|
52
|
+
return_set.add(i.__getattr__('meta')[attribute])
|
|
53
|
+
except:
|
|
54
|
+
pass
|
|
55
|
+
else:
|
|
56
|
+
try:
|
|
57
|
+
return_set.add(i.__getattr__(attribute))
|
|
58
|
+
except:
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
return return_set
|